diff --git a/DEPS b/DEPS
index 1749591..76526eb 100644
--- a/DEPS
+++ b/DEPS
@@ -310,15 +310,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1923ef9d62d4cfdf64806784e324c88c91634fea',
+  'skia_revision': '4e51918bba1fe22900ab054934c7fe22ed648cda',
   # 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': '48d1838e9a69acd2a160e100abf7fb1337410e34',
+  'v8_revision': '600fab3b6cffe654175bb0ec9ab8c75fa7778c9d',
   # 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': '44adf8c68342a12175553c7164952c8b05988e19',
+  'angle_revision': 'd78f7d20627effb75a5e1f10608b77502e70beef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -381,7 +381,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '0a4b3b9b9c23669dfb1a3c8a11d055c1fd2f37c3',
+  'catapult_revision': '5b90d5e7568beef38c375ca5787802ee37fc50c9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -389,7 +389,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'b520cf621422630fd8a697b5c790247a2554f0ee',
+  'devtools_frontend_revision': '822c80abac6ccbb3a30e1a6c3b97aaa1be2e64e3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -425,7 +425,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': 'f2c1d0aa5bd8d91d49ee7e2726186a66500f2a39',
+  'dawn_revision': '9087a5c28dfdb8f86f588fd6b0aeaa1fd11355a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -473,7 +473,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.
-  'libunwind_revision':    '77b82eb53fb4f942f3d1c5407a30fccf7b7aeb93',
+  'libunwind_revision':    '7ff728a9779cdc8e0639c032dc45cc20f7b4af21',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -826,7 +826,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '572585b60a0344363e5bf1808558ac064a0937ed',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '1bbb14cafb73aeb071b946ffd89f06d6abb53887',
       'condition': 'checkout_ios',
   },
 
@@ -1235,7 +1235,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'b32f181e3feb884ca17257bbd47703a94887a161',
+      'url': 'https://chrome-internal.googlesource.com/devtools/devtools-internal.git' + '@' + 'a8a47f90d9997abf4736c0528001f84cd2f6e158',
     'condition': 'checkout_src_internal',
   },
 
@@ -1623,7 +1623,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '40b17af5c58d2777097eef8fb8a7d61c8419af7b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b2829a1f2f115581d5ab091770e7c1878c243b82',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1775,7 +1775,7 @@
 
   # Display server protocol for Linux.
   'src/third_party/wayland/src': {
-      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + 'e60398b1755bfcdf09f040d3769131fe0d9762fc',
+      'url': Var('chromium_git') + '/external/anongit.freedesktop.org/git/wayland/wayland.git' + '@' + '75c1a93e2067220fa06208f20f8f096bb463ec08',
       'condition': 'checkout_linux',
   },
 
@@ -1805,10 +1805,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0860ad6114aa63cbfdd2a1f79c91cc1e96091621',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '09db8676589efc7003d5a2f219b3de8e7f85a652',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3680605caadfda1aaedd5bfa6947ec635b03e6e9',
+    Var('webrtc_git') + '/src.git' + '@' + 'f68a06c34b79c01137d4f1178627e6af853bbd5f',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1881,7 +1881,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bbe17dcabb966a6eb7e276b534611f728b8e3bc5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0d0825928fc9025796f965aeb87efffa23c02b9a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/OWNERS b/OWNERS
index c95403d0..c02b7ec 100644
--- a/OWNERS
+++ b/OWNERS
@@ -18,7 +18,8 @@
 per-file .gn=file://build/OWNERS
 per-file .mailmap=*
 per-file .rustfmt.toml=file://styleguide/rust/OWNERS
-per-file .vpython*=iannucci@chromium.org
+per-file .vpython3=bpastene@chromium.org
+per-file .vpython3=tikuta@chromium.org
 per-file .yapfignore=*
 per-file AUTHORS=*
 per-file BUILD.gn=file://build/OWNERS
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2061661..3caa4211 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -757,13 +757,81 @@
         'base::RepeatingCallback, which directly support Chromium\'s weak ',
         'pointers, ref counting and more.',
       ),
-      # TODO(https://crbug.com/1364577): Change this to an error and use the
-      # exceptions list below.
-      False,
+      True,
       [
           # Has tests that template trait helpers don't unintentionally match
           # std::function.
-          r"base[\\/]functional[\\/]callback_helpers_unittest\.cc",
+          r'base/functional/callback_helpers_unittest\.cc',
+          # Required to implement interfaces from the third-party perfetto
+          # library.
+          r'base/tracing/perfetto_task_runner\.cc',
+          r'base/tracing/perfetto_task_runner\.h',
+          # Needed for interop with the third-party nearby library type
+          # location::nearby::connections::ResultCallback.
+          'chrome/services/sharing/nearby/nearby_connections_conversions\.cc'
+          # Needed for interop with the internal libassistant library.
+          'chromeos/ash/services/libassistant/callback_utils\.h',
+          # Needed for interop with Fuchsia fidl APIs.
+          'fuchsia_web/webengine/browser/context_impl_browsertest\.cc',
+          'fuchsia_web/webengine/browser/cookie_manager_impl_unittest\.cc',
+          'fuchsia_web/webengine/browser/media_player_impl_unittest\.cc',
+          # Required to interop with interfaces from the third-party perfetto
+          # library.
+          'services/tracing/public/cpp/perfetto/custom_event_recorder\.cc',
+          'services/tracing/public/cpp/perfetto/perfetto_traced_process\.cc',
+          'services/tracing/public/cpp/perfetto/perfetto_traced_process\.h',
+          'services/tracing/public/cpp/perfetto/perfetto_tracing_backend\.cc',
+          'services/tracing/public/cpp/perfetto/producer_client\.cc',
+          'services/tracing/public/cpp/perfetto/producer_client\.h',
+          'services/tracing/public/cpp/perfetto/producer_test_utils\.cc',
+          'services/tracing/public/cpp/perfetto/producer_test_utils\.h',
+          # Required for interop with the third-party webrtc library.
+          'third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl\.cc',
+          'third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl\.h',
+
+          # TODO(https://crbug.com/1364577): Various uses that should be
+          # migrated to something else.
+          # Should use base::OnceCallback or base::RepeatingCallback.
+          'base/allocator/dispatcher/initializer_unittest\.cc',
+          'chrome/browser/ash/accessibility/speech_monitor\.cc',
+          'chrome/browser/ash/accessibility/speech_monitor\.h',
+          'chrome/browser/ash/login/ash_hud_login_browsertest\.cc',
+          'chromecast/base/observer_unittest\.cc',
+          'chromecast/browser/cast_web_view\.h',
+          'chromecast/public/cast_media_shlib\.h',
+          'device/bluetooth/floss/exported_callback_manager\.h',
+          'device/bluetooth/floss/floss_dbus_client\.h',
+          'device/fido/cable/v2_handshake_unittest\.cc',
+          'device/fido/pin\.cc',
+          'services/tracing/perfetto/test_utils\.h',
+          # Should use base::FunctionRef.
+          'chrome/browser/media/webrtc/test_stats_dictionary\.cc',
+          'chrome/browser/media/webrtc/test_stats_dictionary\.h',
+          'chromeos/ash/services/libassistant/device_settings_controller\.cc',
+          'components/browser_ui/client_certificate/android/ssl_client_certificate_request\.cc',
+          'components/gwp_asan/client/sampling_malloc_shims_unittest\.cc',
+          'content/browser/font_unique_name_lookup/font_unique_name_lookup_unittest\.cc',
+          # Does not need std::function at all.
+          'components/omnibox/browser/autocomplete_result\.cc',
+          'device/fido/win/webauthn_api\.cc',
+          'media/audio/alsa/alsa_util\.cc',
+          'media/remoting/stream_provider\.h',
+          'sql/vfs_wrapper\.cc',
+          # TODO(https://crbug.com/1364585): Remove usage and exception list
+          # entries.
+          'extensions/renderer/api/automation/automation_internal_custom_bindings\.cc',
+          'extensions/renderer/api/automation/automation_internal_custom_bindings\.h',
+          # TODO(https://crbug.com/1364579): Remove usage and exception list
+          # entry.
+          'ui/views/controls/focus_ring\.h',
+
+          # Various pre-existing uses in //tools that is low-priority to fix.
+          'tools/binary_size/libsupersize/viewer/caspian/diff\.cc',
+          'tools/binary_size/libsupersize/viewer/caspian/model\.cc',
+          'tools/binary_size/libsupersize/viewer/caspian/model\.h',
+          'tools/binary_size/libsupersize/viewer/caspian/tree_builder\.h',
+          'tools/clang/base_bind_rewriters/BaseBindRewriters\.cpp',
+
           # Not an error in third_party folders.
           _THIRD_PARTY_EXCEPT_BLINK
       ],
diff --git a/android_webview/browser/gfx/root_frame_sink.cc b/android_webview/browser/gfx/root_frame_sink.cc
index 0fcebebe7..c5cbd9c 100644
--- a/android_webview/browser/gfx/root_frame_sink.cc
+++ b/android_webview/browser/gfx/root_frame_sink.cc
@@ -7,6 +7,7 @@
 #include "android_webview/browser/gfx/child_frame.h"
 #include "android_webview/browser/gfx/display_scheduler_webview.h"
 #include "android_webview/browser/gfx/viz_compositor_thread_runner_webview.h"
+#include "base/containers/contains.h"
 #include "base/memory/raw_ptr.h"
 #include "base/trace_event/trace_event.h"
 #include "components/viz/common/features.h"
@@ -433,12 +434,10 @@
 }
 
 void RootFrameSink::OnCaptureStarted(const viz::FrameSinkId& frame_sink_id) {
-  auto it = std::find_if(contained_surfaces_.begin(), contained_surfaces_.end(),
-                         [frame_sink_id](const viz::SurfaceId& surface_id) {
-                           return surface_id.frame_sink_id() == frame_sink_id;
-                         });
-  if (it == contained_surfaces_.end())
+  if (!base::Contains(contained_surfaces_, frame_sink_id,
+                      &viz::SurfaceId::frame_sink_id)) {
     return;
+  }
   // When a capture is started we need to force an invalidate.
   if (client_)
     client_->Invalidate();
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index c2d081d..24d520cfb 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -164,6 +164,22 @@
   return ((index.slot + 1) % cols) == 0;
 }
 
+bool IsIndexMovingFromOneEdgeToAnother(GridIndex old_index,
+                                       GridIndex new_index,
+                                       int cols) {
+  return (IsIndexOnLeftEdge(new_index, cols) &&
+          IsIndexOnRightEdge(old_index, cols)) ||
+         (IsIndexOnLeftEdge(old_index, cols) &&
+          IsIndexOnRightEdge(new_index, cols));
+}
+
+bool IsIndexMovingToDifferentRow(GridIndex old_index,
+                                 GridIndex new_index,
+                                 int cols) {
+  return old_index.slot / cols != new_index.slot / cols ||
+         old_index.page != new_index.page;
+}
+
 }  // namespace
 
 // ImageView for the item icon.
@@ -1118,11 +1134,11 @@
 
   if (most_recent_grid_index_.IsValid()) {
     // Pending row changes are only flagged when the item index changes from one
-    // edge of the grid to the other.
-    if ((IsIndexOnLeftEdge(new_grid_index, columns) &&
-         IsIndexOnRightEdge(most_recent_grid_index_, columns)) ||
-        (IsIndexOnLeftEdge(most_recent_grid_index_, columns) &&
-         IsIndexOnRightEdge(new_grid_index, columns))) {
+    // edge of the grid to the other and into a different row.
+    if (IsIndexMovingFromOneEdgeToAnother(most_recent_grid_index_,
+                                          new_grid_index, columns) &&
+        IsIndexMovingToDifferentRow(most_recent_grid_index_, new_grid_index,
+                                    columns)) {
       has_pending_row_change_ = true;
     } else {
       has_pending_row_change_ = false;
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index a022f12f..f9370cd 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -655,8 +655,8 @@
   }
 
   // Get the number of item layer copies used for the between row animation.
-  int GetNumberOfRowChangeLayersForTest() {
-    return apps_grid_view_->row_change_animator_
+  int GetNumberOfRowChangeLayersForTest(AppsGridView* apps_grid_view) {
+    return apps_grid_view->row_change_animator_
         ->GetNumberOfRowChangeLayersForTest();
   }
 
@@ -844,7 +844,7 @@
 
   GetPaginationModel()->SelectPage(1 /*page*/, false /*animate*/);
   EXPECT_EQ(1, GetSelectedPage(paged_apps_grid_view_));
-  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 
   // Begin dragging the third item of the second page.
   InitiateDragForItemAtCurrentPageAt(AppsGridView::MOUSE, 0, 2,
@@ -880,7 +880,7 @@
   EXPECT_EQ(GridIndex(0, 1), paged_apps_grid_view_->reorder_placeholder());
 
   // Four items should have a layer copy used for animating between rows.
-  EXPECT_EQ(4, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(4, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 
   for (size_t i = 1; i < view_model->view_size(); i++) {
     AppListItemView* item_view = view_model->view_at(i);
@@ -905,7 +905,53 @@
   // End the drag and check that no more item layer copies remain.
   EndDrag(apps_grid_view_, false /*cancel*/);
   test_api_->WaitForItemMoveAnimationDone();
-  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
+}
+
+// Test that, within a 4 item folder, a row change animation only triggers when
+// moving an item between rows and not when moving from one side of the grid to
+// the other side in the same row.
+TEST_P(AppsGridViewClamshellAndTabletTest, InFolderBetweenRowsAnimation) {
+  AppListFolderItem* folder_item = model_->CreateAndPopulateFolderWithApps(4);
+
+  // Record the bounds of the folder view with 4 items in it.
+  AppsGridView* items_grid_view = app_list_folder_view()->items_grid_view();
+
+  // Open the folder
+  test_api_->PressItemAt(0);
+  EXPECT_TRUE(GetAppListTestHelper()->IsInFolderView());
+  EXPECT_EQ(4u, folder_item->ChildItemCount());
+
+  auto* dragged_item_view = items_grid_view->GetItemViewAt(0);
+  auto* generator = GetEventGenerator();
+
+  // Start dragging the item at slot 0.
+  generator->MoveMouseTo(
+      dragged_item_view->GetIconBoundsInScreen().CenterPoint());
+  generator->PressLeftButton();
+  dragged_item_view->FireMouseDragTimerForTest();
+
+  ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  // Drag the item over slot 1.
+  generator->MoveMouseTo(
+      items_grid_view->GetItemViewAt(1)->GetBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(items_grid_view->reorder_timer_for_test()->IsRunning());
+  items_grid_view->reorder_timer_for_test()->FireNow();
+
+  // Check that there is no row change animation for a drag from slot 0 to
+  // slot 1.
+  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest(items_grid_view));
+  // Drag the item over slot 2.
+  generator->MoveMouseTo(
+      items_grid_view->GetItemViewAt(2)->GetBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(items_grid_view->reorder_timer_for_test()->IsRunning());
+  items_grid_view->reorder_timer_for_test()->FireNow();
+
+  // Check that there is a row change animation for a drag from slot 1 to
+  // slot 2.
+  EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest(items_grid_view));
 }
 
 // Test dragging an app item from the first row to second row, and then back to
@@ -915,7 +961,7 @@
   model_->PopulateApps(GetTilesPerPage(0));
   UpdateLayout();
 
-  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 
   // Begin dragging the first item.
   InitiateDragForItemAtCurrentPageAt(AppsGridView::MOUSE, 0, 0,
@@ -945,7 +991,7 @@
   const int first_row_y = GetItemRectOnCurrentPageAt(0, 0).y();
   const int second_row_y = GetItemRectOnCurrentPageAt(0, 6).y();
   EXPECT_GT(second_row_y, first_row_y);
-  EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 
   // The item in slot 5 should now be on animating into the first row position.
   EXPECT_EQ(item_view->bounds().y(), first_row_y);
@@ -979,12 +1025,12 @@
           item_view);
   EXPECT_LT(item_view->bounds().x(), target_bounds.x());
   EXPECT_EQ(target_bounds.y(), second_row_y);
-  EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 
   // End the drag and check that no more item layer copies remain.
   EndDrag(apps_grid_view_, false /*cancel*/);
   test_api_->WaitForItemMoveAnimationDone();
-  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest());
+  EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest(apps_grid_view_));
 }
 
 TEST_F(AppsGridViewTest, ItemTooltip) {
diff --git a/ash/capture_mode/capture_mode_feature_pod_controller.cc b/ash/capture_mode/capture_mode_feature_pod_controller.cc
index 3040f77b..168a752 100644
--- a/ash/capture_mode/capture_mode_feature_pod_controller.cc
+++ b/ash/capture_mode/capture_mode_feature_pod_controller.cc
@@ -6,12 +6,14 @@
 
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/capture_mode/capture_mode_metrics.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_tray_item_uma_type.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/feature_pod_controller_base.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -37,7 +39,13 @@
   return button_;
 }
 
+QsFeatureCatalogName CaptureModeFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kCaptureMode;
+}
+
 void CaptureModeFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(/*target_toggle_state=*/true);
+
   // Close the system tray bubble. Deletes |this|.
   tray_controller_->CloseBubble();
 
diff --git a/ash/capture_mode/capture_mode_feature_pod_controller.h b/ash/capture_mode/capture_mode_feature_pod_controller.h
index c520f5e1..b761ebbd 100644
--- a/ash/capture_mode/capture_mode_feature_pod_controller.h
+++ b/ash/capture_mode/capture_mode_feature_pod_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_FEATURE_POD_CONTROLLER_H_
 #define ASH_CAPTURE_MODE_CAPTURE_MODE_FEATURE_POD_CONTROLLER_H_
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
 namespace ash {
@@ -24,6 +25,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc
index 1b2cc3756..24d9b54 100644
--- a/ash/components/arc/arc_features.cc
+++ b/ash/components/arc/arc_features.cc
@@ -60,7 +60,7 @@
 // also whether or not TTS cache is used.
 BASE_FEATURE(kEnableTTSCacheSetup,
              "ArcEnableTTSCacheSetup",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Controls whether we should delegate audio focus requests from ARC to Chrome.
 BASE_FEATURE(kEnableUnifiedAudioFocusFeature,
diff --git a/ash/components/arc/session/arc_session_impl_unittest.cc b/ash/components/arc/session/arc_session_impl_unittest.cc
index 8d90c19..70d6565 100644
--- a/ash/components/arc/session/arc_session_impl_unittest.cc
+++ b/ash/components/arc/session/arc_session_impl_unittest.cc
@@ -901,23 +901,23 @@
       GetClient(arc_session.get())->last_start_params().disable_ureadahead);
 }
 
-// Test that validates TTS caching is enabled by default.
+// Test that validates TTS caching is disabled by default.
 TEST_F(ArcSessionImplTest, TTSCachingByDefault) {
   auto arc_session = CreateArcSession();
   arc_session->StartMiniInstance();
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(
+  EXPECT_FALSE(
       GetClient(arc_session.get())->last_start_params().enable_tts_caching);
 }
 
-// Test that validates TTS caching is disabled.
+// Test that validates TTS caching is enabled.
 TEST_F(ArcSessionImplTest, TTSCachingEnabled) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, false);
+  feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, true);
   auto arc_session = CreateArcSession();
   arc_session->StartMiniInstance();
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(
+  EXPECT_TRUE(
       GetClient(arc_session.get())->last_start_params().enable_tts_caching);
 }
 
diff --git a/ash/components/arc/session/arc_upgrade_params_unittest.cc b/ash/components/arc/session/arc_upgrade_params_unittest.cc
index 9e89029..a0e9b58 100644
--- a/ash/components/arc/session/arc_upgrade_params_unittest.cc
+++ b/ash/components/arc/session/arc_upgrade_params_unittest.cc
@@ -24,14 +24,14 @@
 
 TEST(ArcUpgradeParamsTest, Constructor_DefaultTtsState) {
   UpgradeParams upgradeParams;
-  EXPECT_FALSE(upgradeParams.skip_tts_cache);
+  EXPECT_TRUE(upgradeParams.skip_tts_cache);
 }
 
 TEST(ArcUpgradeParamsTest, Constructor_WithTtsCacheSetupFeatureDisabled) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, false);
+  feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, true);
   UpgradeParams upgradeParams;
-  EXPECT_TRUE(upgradeParams.skip_tts_cache);
+  EXPECT_FALSE(upgradeParams.skip_tts_cache);
 }
 
 }  // namespace
diff --git a/ash/constants/quick_settings_catalogs.h b/ash/constants/quick_settings_catalogs.h
index f8fc70d..32a485d 100644
--- a/ash/constants/quick_settings_catalogs.h
+++ b/ash/constants/quick_settings_catalogs.h
@@ -28,6 +28,32 @@
   kMaxValue = kVersionButton
 };
 
+// A catalog that registers all the features on the Quick Settings page. This
+// catalog should be kept in sync with the buttons on the Quick Settings page.
+// Current values should not be renumbered or removed, because they are recorded
+// in histograms (histograms' enums.xml `QsFeatureCatalogName`). To deprecate
+// use `_DEPRECATED` post-fix on the name.
+enum class QsFeatureCatalogName {
+  kUnknown = 0,
+  kNetwork = 1,
+  kBluetooth = 2,
+  kAccessibility = 3,
+  kQuietMode = 4,
+  kRotationLock = 5,
+  kPrivacyScreen = 6,
+  kCaptureMode = 7,
+  kNearbyShare = 8,
+  kNightLight = 9,
+  kCast = 10,
+  kVPN = 11,
+  kIME = 12,
+  kLocale = 13,
+  kDarkMode = 14,
+  kShelfParty = 15,
+  kAutozoom = 16,
+  kMaxValue = kAutozoom
+};
+
 }  // namespace ash
 
 #endif  // ASH_CONSTANTS_QUICK_SETTINGS_CATALOGS_H_
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index e887496..d59e2d0 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -2457,7 +2457,7 @@
   if (ash::features::IsCryptohomeRecoveryFlowEnabled()) {
     user_manager::KnownUser(Shell::Get()->local_state())
         .UpdateReauthReason(account_id,
-                            static_cast<int>(ReauthReason::FORGOT_PASSWORD));
+                            static_cast<int>(ReauthReason::kForgotPassword));
   }
   Shell::Get()->login_screen_controller()->ShowGaiaSignin(account_id);
   HideAuthErrorMessage();
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 820bfcd..c610a5b 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -1290,9 +1290,7 @@
   absl::optional<int> reauth_reason =
       user_manager::KnownUser(Shell::Get()->local_state())
           .FindReauthReason(users()[0].basic_user_info.account_id);
-  EXPECT_TRUE(reauth_reason.has_value());
-  EXPECT_EQ(reauth_reason.value(),
-            static_cast<int>(ReauthReason::FORGOT_PASSWORD));
+  EXPECT_EQ(reauth_reason, static_cast<int>(ReauthReason::kForgotPassword));
 }
 
 // Gaia is never shown on lock, no mater how many times auth fails.
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 66c1372..1cadc7b 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -150,6 +150,8 @@
     "holding_space/holding_space_prefs.h",
     "holding_space/holding_space_progress.cc",
     "holding_space/holding_space_progress.h",
+    "holding_space/holding_space_section.cc",
+    "holding_space/holding_space_section.h",
     "holding_space/holding_space_util.cc",
     "holding_space/holding_space_util.h",
     "image_downloader.cc",
@@ -448,6 +450,7 @@
     "holding_space/holding_space_item_unittest.cc",
     "holding_space/holding_space_model_unittest.cc",
     "holding_space/holding_space_progress_unittest.cc",
+    "holding_space/holding_space_section_unittest.cc",
     "holding_space/holding_space_util_unittest.cc",
     "metrics_util_unittest.cc",
     "pagination/pagination_model_unittest.cc",
diff --git a/ash/public/cpp/holding_space/holding_space_constants.h b/ash/public/cpp/holding_space/holding_space_constants.h
index e96969d1..bc06365 100644
--- a/ash/public/cpp/holding_space/holding_space_constants.h
+++ b/ash/public/cpp/holding_space/holding_space_constants.h
@@ -88,19 +88,6 @@
 // Note that this is not enforced for pinned items.
 constexpr base::TimeDelta kMaxFileAge = base::Days(1);
 
-// The maximum allowed number of downloads to display in holding space UI.
-constexpr size_t kMaxDownloads = 4u;
-
-// The maximum allowed number of screen captures to display in holding space UI.
-constexpr size_t kMaxScreenCaptures = 3u;
-
-// The maximum allowed number of suggestions to display in holding space UI.
-constexpr size_t kMaxSuggestions = 4u;
-
-// The maximum number of items to store in the `HoldingSpaceModel` for screen
-// captures and downloads.
-constexpr size_t kMaxItemsPerSection = 50;
-
 // Mime type with wildcard which matches all image types.
 constexpr char kMimeTypeImage[] = "image/*";
 
diff --git a/ash/public/cpp/holding_space/holding_space_model.cc b/ash/public/cpp/holding_space/holding_space_model.cc
index 4e937be1..83f771b 100644
--- a/ash/public/cpp/holding_space/holding_space_model.cc
+++ b/ash/public/cpp/holding_space/holding_space_model.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "base/bind.h"
 #include "base/check.h"
@@ -19,23 +20,6 @@
 
 namespace ash {
 
-namespace {
-
-// Helpers ---------------------------------------------------------------------
-
-// Maps a `HoldingSpaceItem::Type` to a `HoldingSpaceModel::Section`.
-HoldingSpaceModel::Section ToSection(HoldingSpaceItem::Type type) {
-  if (holding_space_util::IsDownloadType(type))
-    return HoldingSpaceModel::Section::kDownload;
-
-  if (holding_space_util::IsScreenCaptureType(type))
-    return HoldingSpaceModel::Section::kScreenCapture;
-
-  return HoldingSpaceModel::Section::kNone;
-}
-
-}  // namespace
-
 // HoldingSpaceModel::ScopedItemUpdate -----------------------------------------
 
 HoldingSpaceModel::ScopedItemUpdate::~ScopedItemUpdate() {
@@ -209,8 +193,21 @@
   for (auto& observer : observers_)
     observer.OnHoldingSpaceItemsAdded(item_ptrs);
 
-  if (features::IsHoldingSpacePredictabilityEnabled())
-    TrimToMaxItemsPerSection();
+  if (!features::IsHoldingSpacePredictabilityEnabled())
+    return;
+
+  // When the predictability feature flag is enabled, holding space items do
+  // not automatically expire. Instead, a maximum item count for each section
+  // is enforced such that adding new items may result in removing the oldest
+  // items from the same section.
+  RemoveIf(base::BindRepeating(
+      [](std::map<HoldingSpaceSectionId, size_t>& item_counts_per_section_id,
+         const HoldingSpaceItem* item) {
+        const auto* section = GetHoldingSpaceSection(item->type());
+        const auto item_count = ++item_counts_per_section_id[section->id];
+        return section->max_item_count && item_count > *section->max_item_count;
+      },
+      base::OwnedRef(std::map<HoldingSpaceSectionId, size_t>())));
 }
 
 void HoldingSpaceModel::RemoveItem(const std::string& id) {
@@ -342,22 +339,4 @@
   observers_.RemoveObserver(observer);
 }
 
-// Removes any items that exceed the `kMaxItemsPerSection` of that
-// `HoldingSpaceItem::Type`. Types are bucketed into screen_captures and
-// downloads. For example if `kMaxItemsPerSection` is 10 and after adding 2
-// new download items the user has a total of 12 items in the downloads
-// bucket, then we remove the 2 oldest (i.e. first we find in the vector)
-// downloads from holding space `items_`, leaving the 10 newest remaining.
-// If it's not a download or screen capture then no limit is applied.
-void HoldingSpaceModel::TrimToMaxItemsPerSection() {
-  RemoveIf(base::BindRepeating(
-      [](std::map<Section, size_t>& items_per_section,
-         const HoldingSpaceItem* item) {
-        auto section = ToSection(item->type());
-        return ++items_per_section[section] > kMaxItemsPerSection &&
-               section != HoldingSpaceModel::Section::kNone;
-      },
-      base::OwnedRef(std::map<Section, size_t>())));
-}
-
 }  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_model.h b/ash/public/cpp/holding_space/holding_space_model.h
index 0f5a374..ade57db 100644
--- a/ash/public/cpp/holding_space/holding_space_model.h
+++ b/ash/public/cpp/holding_space/holding_space_model.h
@@ -110,8 +110,6 @@
     bool invalidate_image_ = false;
   };
 
-  enum class Section { kDownload, kScreenCapture, kNone };
-
   HoldingSpaceModel();
   HoldingSpaceModel(const HoldingSpaceModel& other) = delete;
   HoldingSpaceModel& operator=(const HoldingSpaceModel& other) = delete;
@@ -176,9 +174,6 @@
   void RemoveObserver(HoldingSpaceModelObserver* observer);
 
  private:
-  // Removes any items that exceed the `kMaxItemsPerSection` of that section.
-  void TrimToMaxItemsPerSection();
-
   // The list of items added to the model in the order they have been added to
   // the model.
   ItemList items_;
diff --git a/ash/public/cpp/holding_space/holding_space_model_unittest.cc b/ash/public/cpp/holding_space/holding_space_model_unittest.cc
index c0fb7ab..f73ea18 100644
--- a/ash/public/cpp/holding_space/holding_space_model_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_model_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "base/bind.h"
 #include "base/scoped_observation.h"
@@ -150,9 +151,10 @@
 // HoldingSpaceModelTest -------------------------------------------------------
 
 // Base class for `HoldingSpaceModel` tests, parameterized by the set of all
-// holding space item types.
+// holding space item types and whether the predictability feature is enabled.
 class HoldingSpaceModelTest
-    : public testing::TestWithParam<std::tuple<HoldingSpaceItem::Type, bool>> {
+    : public testing::TestWithParam<
+          std::tuple<HoldingSpaceItem::Type, /*predictability_enabled=*/bool>> {
  public:
   HoldingSpaceModelTest() {
     scoped_feature_list_.InitWithFeatureState(
@@ -163,12 +165,12 @@
   // Returns the `HoldingSpaceModel` under test.
   HoldingSpaceModel& model() { return model_; }
 
-  bool IsHoldingSpacePredictabilityEnabled() const {
-    return std::get<1>(GetParam());
+  HoldingSpaceItem::Type GetHoldingSpaceItemType() const {
+    return std::get<0>(GetParam());
   }
 
-  HoldingSpaceItem::Type GetHoldingSpaceItemType() {
-    return std::get<0>(GetParam());
+  bool IsHoldingSpacePredictabilityEnabled() const {
+    return std::get<1>(GetParam());
   }
 
  private:
@@ -180,7 +182,7 @@
     All,
     HoldingSpaceModelTest,
     testing::Combine(testing::ValuesIn(GetHoldingSpaceItemTypes()),
-                     testing::Bool()));
+                     /*predictability_enabled=*/testing::Bool()));
 
 // Tests -----------------------------------------------------------------------
 
@@ -564,52 +566,56 @@
   EXPECT_TRUE(item_ptr->progress().IsComplete());
 }
 
-TEST_P(HoldingSpaceModelTest,
-       AddingItemsPushesOutOldestItemsWhenMaxItemsPerSectionReached) {
+TEST_P(HoldingSpaceModelTest, EnforcesMaxItemCountsPerSection) {
   ScopedModelObservation observation(&model());
 
   // Verify the `model()` is initially empty.
   EXPECT_EQ(model().items().size(), 0u);
 
-  // Add 2 items and store their id's to assert that they were deleted later.
-  auto first_item = CreateItem(GetHoldingSpaceItemType());
-  std::string first_item_id = first_item.get()->id();
-  model().AddItem(std::move(first_item));
-  auto second_item = CreateItem(GetHoldingSpaceItemType());
-  std::string second_item_id = second_item.get()->id();
-  model().AddItem(std::move(second_item));
+  // Cache the section to which the parameterized type belongs.
+  const HoldingSpaceSection* section =
+      GetHoldingSpaceSection(GetHoldingSpaceItemType());
+  ASSERT_TRUE(section);
 
-  // Starting at index 2, add more items up to `kMaxItemsPerSection`.
-  for (size_t i = 2; i < kMaxItemsPerSection; ++i) {
-    // Add `item` to the `model()`.
+  // Add the maximum count of items allowed for the section or some high number
+  // if the section does not specify a maximum item count restriction.
+  constexpr size_t kMaxItemCount = 50u;
+  for (size_t i = 0u; i < section->max_item_count.value_or(kMaxItemCount); ++i)
     model().AddItem(CreateItem(GetHoldingSpaceItemType()));
-  }
-  // Verify we've reached the limit of `items` in the model.
-  EXPECT_EQ(model().items().size(), kMaxItemsPerSection);
+  ASSERT_EQ(model().items().size(),
+            section->max_item_count.value_or(kMaxItemCount));
 
-  // Add 2 more `item`s which takes us to `kMaxItemsPerSection` + 2.
-  constexpr int kExtraItems = 2;
-  for (int i = 0; i < kExtraItems; ++i)
-    model().AddItem(CreateItem(GetHoldingSpaceItemType()));
+  // Cache the IDs of items which may be expected to be removed later.
+  constexpr size_t kExtraItemCount = 2u;
+  ASSERT_GE(model().items().size(), kExtraItemCount);
+  std::vector<std::string> item_ids;
+  for (int i = kExtraItemCount - 1; i >= 0; --i)
+    item_ids.push_back(model().items()[i]->id());
 
-  // If the `type` is contained inside the screen capture or downloads
-  // sections and the feature flag is enabled, then assert that we have no more
-  // than the `kMaxItemsPerSection` and that the oldest item(s) were deleted.
-  if (IsHoldingSpacePredictabilityEnabled() &&
-      (holding_space_util::IsDownloadType(GetHoldingSpaceItemType()) ||
-       holding_space_util::IsScreenCaptureType(GetHoldingSpaceItemType()))) {
-    EXPECT_EQ(model().items().size(), kMaxItemsPerSection);
-    EXPECT_EQ(model().GetItem(first_item_id), nullptr);
-    EXPECT_EQ(model().GetItem(second_item_id), nullptr);
+  // Add extra items of the same type to the model.
+  std::vector<std::unique_ptr<HoldingSpaceItem>> items;
+  for (size_t i = 0u; i < kExtraItemCount; ++i)
+    items.push_back(CreateItem(GetHoldingSpaceItemType()));
+  model().AddItems(std::move(items));
+
+  // Cache a lambda to return whether the `model()` contains an item for `id`.
+  auto model_contains_item_for_id = [&](const std::string& id) -> bool {
+    return model().GetItem(id);
+  };
+
+  // If the feature flag is enabled and the section specifies a maximum item
+  // count restriction, assert that the oldest items were removed. Otherwise,
+  // nothing should have been removed from the `model()`.
+  if (IsHoldingSpacePredictabilityEnabled() && section->max_item_count) {
+    EXPECT_EQ(model().items().size(), section->max_item_count);
+    EXPECT_TRUE(base::ranges::none_of(item_ids, model_contains_item_for_id));
     EXPECT_THAT(observation.TakeRemovedItems(),
-                testing::ElementsAre(first_item_id, second_item_id));
+                testing::ElementsAreArray(item_ids));
   } else {
-    // If the `type` is not contained inside the screen capture or downloads
-    // section buckets or the feature flag is disabled then we expect to exceed
-    // the `kMaxItemsPerSection` and not delete anything.
-    EXPECT_EQ(model().items().size(), kMaxItemsPerSection + kExtraItems);
-    EXPECT_TRUE(model().GetItem(first_item_id));
-    EXPECT_TRUE(model().GetItem(second_item_id));
+    EXPECT_EQ(
+        model().items().size(),
+        section->max_item_count.value_or(kMaxItemCount) + kExtraItemCount);
+    EXPECT_TRUE(base::ranges::all_of(item_ids, model_contains_item_for_id));
     EXPECT_TRUE(observation.TakeRemovedItems().empty());
   }
 }
diff --git a/ash/public/cpp/holding_space/holding_space_section.cc b/ash/public/cpp/holding_space/holding_space_section.cc
new file mode 100644
index 0000000..15f9b34
--- /dev/null
+++ b/ash/public/cpp/holding_space/holding_space_section.cc
@@ -0,0 +1,143 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/holding_space/holding_space_section.h"
+
+#include <map>
+
+#include "base/containers/contains.h"
+#include "base/no_destructor.h"
+
+namespace ash {
+namespace {
+
+// Returns whether the specified `sections_by_id` pass validation.
+bool IsValid(const std::map<HoldingSpaceSectionId, HoldingSpaceSection>&
+                 sections_by_id) {
+#if DCHECK_IS_ON()
+  // There should be no overlap of `supported_types` between `section`s.
+  std::set<HoldingSpaceItem::Type> types;
+  for (const auto& [id, section] : sections_by_id) {
+    for (const auto& type : section.supported_types) {
+      if (!types.insert(type).second)
+        return false;
+    }
+  }
+#endif  // DCHECK_IS_ON()
+  return true;
+}
+
+// Creates the map of all holding space sections to their respective IDs.
+std::map<HoldingSpaceSectionId, HoldingSpaceSection> CreateSectionsById() {
+  std::map<HoldingSpaceSectionId, HoldingSpaceSection> sections_by_id;
+
+  // Downloads.
+  sections_by_id.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(HoldingSpaceSectionId::kDownloads),
+      std::forward_as_tuple(
+          /*id=*/HoldingSpaceSectionId::kDownloads,
+          /*supported_types=*/
+          std::set<HoldingSpaceItem::Type>({
+              HoldingSpaceItem::Type::kArcDownload,
+              HoldingSpaceItem::Type::kDiagnosticsLog,
+              HoldingSpaceItem::Type::kDownload,
+              HoldingSpaceItem::Type::kLacrosDownload,
+              HoldingSpaceItem::Type::kNearbyShare,
+              HoldingSpaceItem::Type::kPrintedPdf,
+              HoldingSpaceItem::Type::kScan,
+              HoldingSpaceItem::Type::kPhoneHubCameraRoll,
+          }),
+          /*max_item_count=*/absl::make_optional<size_t>(50u),
+          /*max_visible_item_count=*/
+          absl::make_optional<size_t>(4u)));
+
+  // Pinned files.
+  sections_by_id.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(HoldingSpaceSectionId::kPinnedFiles),
+      std::forward_as_tuple(
+          /*id=*/HoldingSpaceSectionId::kPinnedFiles,
+          /*supported_types=*/
+          std::set<HoldingSpaceItem::Type>({
+              HoldingSpaceItem::Type::kPinnedFile,
+          }),
+          /*max_item_count=*/absl::optional<size_t>(),
+          /*max_visible_item_count=*/absl::optional<size_t>()));
+
+  // Screen captures.
+  sections_by_id.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(HoldingSpaceSectionId::kScreenCaptures),
+      std::forward_as_tuple(
+          /*id=*/HoldingSpaceSectionId::kScreenCaptures,
+          /*supported_types=*/
+          std::set<HoldingSpaceItem::Type>({
+              HoldingSpaceItem::Type::kScreenshot,
+              HoldingSpaceItem::Type::kScreenRecording,
+          }),
+          /*max_item_count=*/absl::make_optional<size_t>(50u),
+          /*max_visible_item_count=*/
+          absl::make_optional<size_t>(3u)));
+
+  // Suggestions.
+  sections_by_id.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(HoldingSpaceSectionId::kSuggestions),
+      std::forward_as_tuple(
+          /*id=*/HoldingSpaceSectionId::kSuggestions,
+          /*supported_types=*/
+          std::set<HoldingSpaceItem::Type>({
+              HoldingSpaceItem::Type::kDriveSuggestion,
+              HoldingSpaceItem::Type::kLocalSuggestion,
+          }),
+          /*max_item_count=*/absl::optional<size_t>(),
+          /*max_visible_item_count=*/
+          absl::make_optional<size_t>(4u)));
+
+  DCHECK(IsValid(sections_by_id));
+  return sections_by_id;
+}
+
+// Returns all holding space sections mapped to their respective IDs.
+std::map<HoldingSpaceSectionId, HoldingSpaceSection>& GetSectionsById() {
+  static base::NoDestructor<
+      std::map<HoldingSpaceSectionId, HoldingSpaceSection>>
+      sections_by_id(CreateSectionsById());
+  return *sections_by_id;
+}
+
+}  // namespace
+
+// HoldingSpaceSection ---------------------------------------------------------
+
+HoldingSpaceSection::HoldingSpaceSection(
+    HoldingSpaceSectionId id,
+    std::set<HoldingSpaceItem::Type> supported_types,
+    absl::optional<size_t> max_item_count,
+    absl::optional<size_t> max_visible_item_count)
+    : id(id),
+      supported_types(std::move(supported_types)),
+      max_item_count(max_item_count),
+      max_visible_item_count(max_visible_item_count) {}
+
+HoldingSpaceSection::~HoldingSpaceSection() = default;
+
+// Utilities -------------------------------------------------------------------
+
+const HoldingSpaceSection* GetHoldingSpaceSection(HoldingSpaceItem::Type type) {
+  for (const auto& [id, section] : GetSectionsById()) {
+    if (base::Contains(section.supported_types, type))
+      return &section;
+  }
+  return nullptr;
+}
+
+const HoldingSpaceSection* GetHoldingSpaceSection(HoldingSpaceSectionId id) {
+  const auto& sections_by_id = GetSectionsById();
+  const auto it = sections_by_id.find(id);
+  return it != sections_by_id.end() ? &it->second : nullptr;
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_section.h b/ash/public/cpp/holding_space/holding_space_section.h
new file mode 100644
index 0000000..98b9d6a
--- /dev/null
+++ b/ash/public/cpp/holding_space/holding_space_section.h
@@ -0,0 +1,61 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_SECTION_H_
+#define ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_SECTION_H_
+
+#include <set>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+
+namespace ash {
+
+// Enumeration of unique identifiers for a holding space section.
+enum class HoldingSpaceSectionId {
+  kMinValue = 0,
+  kDownloads = kMinValue,
+  kPinnedFiles,
+  kScreenCaptures,
+  kSuggestions,
+  kMaxValue = kSuggestions,
+};
+
+// Model for a holding space section.
+struct ASH_PUBLIC_EXPORT HoldingSpaceSection {
+  HoldingSpaceSection(HoldingSpaceSectionId id,
+                      std::set<HoldingSpaceItem::Type> supported_types,
+                      absl::optional<size_t> max_item_count,
+                      absl::optional<size_t> max_visible_item_count);
+
+  HoldingSpaceSection(const HoldingSpaceSection&) = delete;
+  HoldingSpaceSection& operator=(const HoldingSpaceSection&) = delete;
+  ~HoldingSpaceSection();
+
+  // Unique identifier for the section.
+  const HoldingSpaceSectionId id;
+
+  // Types of holding space items to be rendered in the section.
+  const std::set<HoldingSpaceItem::Type> supported_types;
+
+  // Maximum count of items to be cached in the model for the section. If
+  // absent, no maximum count is enforced.
+  const absl::optional<size_t> max_item_count;
+
+  // Maximum count of items to be visible at once for the section in holding
+  // space UI. If absent, no maximum count is enforced.
+  const absl::optional<size_t> max_visible_item_count;
+};
+
+// Returns the section to which the specified `type` belongs.
+ASH_PUBLIC_EXPORT const HoldingSpaceSection* GetHoldingSpaceSection(
+    HoldingSpaceItem::Type type);
+
+// Returns the section uniquely identified by the specified `id`.
+ASH_PUBLIC_EXPORT const HoldingSpaceSection* GetHoldingSpaceSection(
+    HoldingSpaceSectionId id);
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_HOLDING_SPACE_HOLDING_SPACE_SECTION_H_
diff --git a/ash/public/cpp/holding_space/holding_space_section_unittest.cc b/ash/public/cpp/holding_space/holding_space_section_unittest.cc
new file mode 100644
index 0000000..02ed7140
--- /dev/null
+++ b/ash/public/cpp/holding_space/holding_space_section_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/holding_space/holding_space_section.h"
+
+#include <set>
+
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace {
+
+// Helpers ---------------------------------------------------------------------
+
+std::set<HoldingSpaceItem::Type> GetHoldingSpaceItemTypes() {
+  std::set<HoldingSpaceItem::Type> types;
+  for (size_t i = 0u;
+       i <= static_cast<size_t>(HoldingSpaceItem::Type::kMaxValue); ++i) {
+    types.emplace(static_cast<HoldingSpaceItem::Type>(i));
+  }
+  return types;
+}
+
+std::set<HoldingSpaceSectionId> GetHoldingSpaceSectionIds() {
+  std::set<HoldingSpaceSectionId> section_ids;
+  for (size_t i = static_cast<size_t>(HoldingSpaceSectionId::kMinValue);
+       i <= static_cast<size_t>(HoldingSpaceSectionId::kMaxValue); ++i) {
+    section_ids.emplace(static_cast<HoldingSpaceSectionId>(i));
+  }
+  return section_ids;
+}
+
+void ExpectSection(const HoldingSpaceSection* section,
+                   HoldingSpaceSectionId expected_id) {
+  ASSERT_TRUE(section);
+  switch (expected_id) {
+    case HoldingSpaceSectionId::kDownloads:
+      EXPECT_EQ(section->id, HoldingSpaceSectionId::kDownloads);
+      EXPECT_THAT(section->supported_types,
+                  testing::UnorderedElementsAre(
+                      HoldingSpaceItem::Type::kArcDownload,
+                      HoldingSpaceItem::Type::kDiagnosticsLog,
+                      HoldingSpaceItem::Type::kDownload,
+                      HoldingSpaceItem::Type::kLacrosDownload,
+                      HoldingSpaceItem::Type::kNearbyShare,
+                      HoldingSpaceItem::Type::kPhoneHubCameraRoll,
+                      HoldingSpaceItem::Type::kPrintedPdf,
+                      HoldingSpaceItem::Type::kScan));
+      EXPECT_EQ(section->max_item_count, 50u);
+      EXPECT_EQ(section->max_visible_item_count, 4u);
+      break;
+    case HoldingSpaceSectionId::kPinnedFiles:
+      EXPECT_EQ(section->id, HoldingSpaceSectionId::kPinnedFiles);
+      EXPECT_THAT(
+          section->supported_types,
+          testing::UnorderedElementsAre(HoldingSpaceItem::Type::kPinnedFile));
+      EXPECT_EQ(section->max_item_count, absl::nullopt);
+      EXPECT_EQ(section->max_visible_item_count, absl::nullopt);
+      break;
+    case HoldingSpaceSectionId::kScreenCaptures:
+      EXPECT_EQ(section->id, HoldingSpaceSectionId::kScreenCaptures);
+      EXPECT_THAT(section->supported_types,
+                  testing::UnorderedElementsAre(
+                      HoldingSpaceItem::Type::kScreenRecording,
+                      HoldingSpaceItem::Type::kScreenshot));
+      EXPECT_EQ(section->max_item_count, 50u);
+      EXPECT_EQ(section->max_visible_item_count, 3u);
+      break;
+    case HoldingSpaceSectionId::kSuggestions:
+      EXPECT_EQ(section->id, HoldingSpaceSectionId::kSuggestions);
+      EXPECT_THAT(section->supported_types,
+                  testing::UnorderedElementsAre(
+                      HoldingSpaceItem::Type::kLocalSuggestion,
+                      HoldingSpaceItem::Type::kDriveSuggestion));
+      EXPECT_EQ(section->max_item_count, absl::nullopt);
+      EXPECT_EQ(section->max_visible_item_count, 4u);
+      break;
+  }
+}
+
+}  // namespace
+
+// Tests -----------------------------------------------------------------------
+
+using HoldingSpaceSectionTest = testing::Test;
+
+// Verifies that every `HoldingSpaceSectionId` maps to an expected section.
+TEST_F(HoldingSpaceSectionTest, GetHoldingSpaceSectionById) {
+  for (const auto& id : GetHoldingSpaceSectionIds()) {
+    SCOPED_TRACE(testing::Message() << "ID: " << static_cast<size_t>(id));
+    ExpectSection(GetHoldingSpaceSection(id), id);
+  }
+}
+
+// Verifies that every `HoldingSpaceItem::Type` maps to an expected section.
+TEST_F(HoldingSpaceSectionTest, GetHoldingSpaceSectionByType) {
+  for (const auto& type : GetHoldingSpaceItemTypes()) {
+    SCOPED_TRACE(testing::Message() << "Type: " << static_cast<size_t>(type));
+    absl::optional<HoldingSpaceSectionId> id;
+    switch (type) {
+      case HoldingSpaceItem::Type::kArcDownload:
+      case HoldingSpaceItem::Type::kDiagnosticsLog:
+      case HoldingSpaceItem::Type::kDownload:
+      case HoldingSpaceItem::Type::kLacrosDownload:
+      case HoldingSpaceItem::Type::kNearbyShare:
+      case HoldingSpaceItem::Type::kPhoneHubCameraRoll:
+      case HoldingSpaceItem::Type::kPrintedPdf:
+      case HoldingSpaceItem::Type::kScan:
+        id = HoldingSpaceSectionId::kDownloads;
+        break;
+      case HoldingSpaceItem::Type::kDriveSuggestion:
+      case HoldingSpaceItem::Type::kLocalSuggestion:
+        id = HoldingSpaceSectionId::kSuggestions;
+        break;
+      case HoldingSpaceItem::Type::kPinnedFile:
+        id = HoldingSpaceSectionId::kPinnedFiles;
+        break;
+      case HoldingSpaceItem::Type::kScreenRecording:
+      case HoldingSpaceItem::Type::kScreenshot:
+        id = HoldingSpaceSectionId::kScreenCaptures;
+        break;
+    }
+    ExpectSection(GetHoldingSpaceSection(type), id.value());
+  }
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_util.cc b/ash/public/cpp/holding_space/holding_space_util.cc
index 63b69ed..0401d7a 100644
--- a/ash/public/cpp/holding_space/holding_space_util.cc
+++ b/ash/public/cpp/holding_space/holding_space_util.cc
@@ -101,28 +101,4 @@
   }
 }
 
-std::set<HoldingSpaceItem::Type> DownloadSupportedTypes() {
-  return {HoldingSpaceItem::Type::kArcDownload,
-          HoldingSpaceItem::Type::kDiagnosticsLog,
-          HoldingSpaceItem::Type::kDownload,
-          HoldingSpaceItem::Type::kLacrosDownload,
-          HoldingSpaceItem::Type::kNearbyShare,
-          HoldingSpaceItem::Type::kPrintedPdf,
-          HoldingSpaceItem::Type::kScan,
-          HoldingSpaceItem::Type::kPhoneHubCameraRoll};
-}
-
-std::set<HoldingSpaceItem::Type> ScreenCaptureSupportedTypes() {
-  return {HoldingSpaceItem::Type::kScreenshot,
-          HoldingSpaceItem::Type::kScreenRecording};
-}
-
-bool IsDownloadType(HoldingSpaceItem::Type item_type) {
-  return base::Contains(DownloadSupportedTypes(), item_type);
-}
-
-bool IsScreenCaptureType(HoldingSpaceItem::Type item_type) {
-  return base::Contains(ScreenCaptureSupportedTypes(), item_type);
-}
-
 }  // namespace ash::holding_space_util
diff --git a/ash/public/cpp/holding_space/holding_space_util.h b/ash/public/cpp/holding_space/holding_space_util.h
index 2ee55f7..b70ef28 100644
--- a/ash/public/cpp/holding_space/holding_space_util.h
+++ b/ash/public/cpp/holding_space/holding_space_util.h
@@ -40,15 +40,6 @@
 // Returns the string representation of the specified holding space item `type`.
 ASH_PUBLIC_EXPORT std::string ToString(HoldingSpaceItem::Type type);
 
-ASH_PUBLIC_EXPORT std::set<HoldingSpaceItem::Type> DownloadSupportedTypes();
-
-ASH_PUBLIC_EXPORT std::set<HoldingSpaceItem::Type>
-ScreenCaptureSupportedTypes();
-
-ASH_PUBLIC_EXPORT bool IsDownloadType(HoldingSpaceItem::Type item_type);
-
-ASH_PUBLIC_EXPORT bool IsScreenCaptureType(HoldingSpaceItem::Type item_type);
-
 }  // namespace holding_space_util
 }  // namespace ash
 
diff --git a/ash/public/cpp/reauth_reason.h b/ash/public/cpp/reauth_reason.h
index c3c4146..02501db5 100644
--- a/ash/public/cpp/reauth_reason.h
+++ b/ash/public/cpp/reauth_reason.h
@@ -11,64 +11,64 @@
 // This enum is used to define the buckets for an enumerated UMA histogram.
 // Hence, existing enumerated constants should never be reordered, and all new
 // constants should only be appended at the end of the  enumeration.
-enum ReauthReason {
+enum class ReauthReason {
   // Default value: no reauth reasons were detected so far, or the reason was
   // already reported.
-  NONE = 0,
+  kNone = 0,
 
   // Legacy profile holders.
-  OTHER = 1,
+  kOther = 1,
 
   // Password changed, revoked credentials, account deleted.
-  INVALID_TOKEN_HANDLE = 2,
+  kInvalidTokenHandle = 2,
 
   // Incorrect password entered 3 times at the user pod.
-  INCORRECT_PASSWORD_ENTERED = 3,
+  kIncorrectPasswordEntered = 3,
 
   // Incorrect password entered by a SAML user once.
   // OS would show a tooltip offering user to complete the online sign-in.
-  INCORRECT_SAML_PASSWORD_ENTERED = 4,
+  kIncorrectSamlPasswordEntered = 4,
 
   // Device policy is set not to show user pods, which requires re-auth on every
   // login.
-  SAML_REAUTH_POLICY = 5,
+  kSamlReauthPolicy = 5,
 
   // Cryptohome is missing, most likely due to deletion during garbage
   // collection.
-  MISSING_CRYPTOHOME = 6,
+  kMissingCryptohome = 6,
 
   // During last login OS failed to connect to the sync with the existing RT.
   // This could be due to account deleted, password changed, account revoked,
   // etc.
-  SYNC_FAILED = 7,
+  kSyncFailed = 7,
 
   // User cancelled the password change prompt when prompted by Chrome OS.
-  PASSWORD_UPDATE_SKIPPED = 8,
+  kPasswordUpdateSkipped = 8,
 
   // SAML password sync token validation failed.
-  SAML_PASSWORD_SYNC_TOKEN_VALIDATION_FAILED = 9,
+  kSamlPasswordSyncTokenValidationFailed = 9,
 
   // Corrupted cryptohome
-  UNRECOVERABLE_CRYPTOHOME = 10,
+  kUnrecoverableCryptohome = 10,
 
   // Gaia policy is set, which requires re-auth on every login if the offline
   // login time limit has been reached.
-  GAIA_REAUTH_POLICY = 11,
+  kGaiaReauthPolicy = 11,
 
   // Gaia lock screen re-auth policy is set, which requires re-auth on lock
   // screen if the offline lock screen time limit has been reached.
-  GAIA_LOCK_SCREEN_REAUTH_POLICY = 12,
+  kGaiaLockScreenReauthPolicy = 12,
 
   // Saml lock screen re-auth policy is set, which requires re-auth on lock
   // screen if the offline lock screen time limit has been reached.
-  SAML_LOCK_SCREEN_REAUTH_POLICY = 13,
+  kSamlLockScreenReauthPolicy = 13,
 
   // "Forgot Password" button clicked on the sign-in screen when password is
   // entered wrongly.
-  FORGOT_PASSWORD = 14,
+  kForgotPassword = 14,
 
   // Must be the last value in this list.
-  NUM_REAUTH_FLOW_REASONS,
+  kNumReauthFlowReasons,
 };
 
 }  // namespace ash
diff --git a/ash/shelf/drag_window_from_shelf_controller.cc b/ash/shelf/drag_window_from_shelf_controller.cc
index 0af0cca..dacea516 100644
--- a/ash/shelf/drag_window_from_shelf_controller.cc
+++ b/ash/shelf/drag_window_from_shelf_controller.cc
@@ -87,12 +87,17 @@
     for (auto* window : windows) {
       if (window == dragged_window_)
         continue;
-      if (::wm::HasTransientAncestor(window, dragged_window_))
+      if (wm::HasTransientAncestor(window, dragged_window_))
         continue;
       if (!window->IsVisible())
         continue;
       if (SplitViewController::Get(window)->IsWindowInSplitView(window))
         continue;
+      auto* overview_controller = Shell::Get()->overview_controller();
+      if (overview_controller->InOverviewSession() &&
+          overview_controller->overview_session()->IsWindowInOverview(window)) {
+        continue;
+      }
 
       hidden_windows_.push_back(window);
       window->AddObserver(this);
@@ -695,11 +700,11 @@
       display::Screen::GetScreen()->GetPrimaryDisplay().id());
 
   (new WindowScaleAnimation(
-       window_, WindowScaleAnimation::WindowScaleType::kScaleDownToShelf,
+       WindowScaleAnimation::WindowScaleType::kScaleDownToShelf,
        base::BindOnce(
            &DragWindowFromShelfController::OnWindowScaledDownAfterDrag,
            weak_ptr_factory_.GetWeakPtr())))
-      ->Start();
+      ->Start(window_);
 }
 
 void DragWindowFromShelfController::OnWindowScaledDownAfterDrag() {
@@ -714,12 +719,12 @@
 
 void DragWindowFromShelfController::ScaleUpToRestoreWindowAfterDrag() {
   (new WindowScaleAnimation(
-       window_, WindowScaleAnimation::WindowScaleType::kScaleUpToRestore,
+       WindowScaleAnimation::WindowScaleType::kScaleUpToRestore,
        base::BindOnce(
            &DragWindowFromShelfController::OnWindowRestoredToOrignalBounds,
            weak_ptr_factory_.GetWeakPtr(),
            /*should_end_overview=*/!started_in_overview_)))
-      ->Start();
+      ->Start(window_);
 }
 
 void DragWindowFromShelfController::OnWindowRestoredToOrignalBounds(
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
index 074b40d..36082a2 100644
--- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -1307,8 +1307,10 @@
   EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window1.get()));
   EXPECT_EQ(split_view_controller()->GetPositionOfSnappedWindow(window1.get()),
             SplitViewController::SnapPosition::kPrimary);
-  // Ensure that the right window is still in the overview.
+  // Ensure that the right window is still in the overview, and doesn't get
+  // minimized.
   EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
+  EXPECT_FALSE(WindowState::Get(window2.get())->IsMinimized());
   EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
 }
 
@@ -1412,6 +1414,35 @@
   EXPECT_FALSE(transient_child_win2->IsVisible());
 }
 
+// Tests that destroying a trasient child that is being dragged from the shelf
+// does not result in a crash. Regression test for https://crbug.com/1200596.
+TEST_F(DragWindowFromShelfControllerTest, DestroyTransientWhileAnimating) {
+  const gfx::Rect shelf_bounds =
+      Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
+
+  // The crash occurred while destroying an animating window.
+  ui::ScopedAnimationDurationScaleMode animation_scale(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+  // The transient child needs to also be an app window.
+  auto window = CreateAppWindow();
+  auto child = CreateAppWindow();
+  wm::AddTransientChild(window.get(), child.get());
+
+  // Drag the child barely above the shelf so that it returns to its original
+  // position on release. The drag can go anywhere as long as the window moves
+  // and the release is close to the top of the shelf.
+  StartDrag(child.get(), shelf_bounds.right_center());
+  Drag(gfx::Point(100, 100), 1.f, 1.f);
+  EndDrag(gfx::Point(shelf_bounds.width() / 2, shelf_bounds.y() - 10),
+          /*velocity_y=*/absl::nullopt);
+  ASSERT_TRUE(window->layer()->GetAnimator()->is_animating());
+  ASSERT_TRUE(child->layer()->GetAnimator()->is_animating());
+
+  // Destroy the transient child during animation. There should be no crash.
+  child.reset();
+}
+
 // Tests that destroying a dragged window in split view will not cause crash.
 TEST_F(DragWindowFromShelfControllerTest,
        DestroyWindowDuringDraggingInSplitView) {
diff --git a/ash/shelf/shelf_party_feature_pod_controller.cc b/ash/shelf/shelf_party_feature_pod_controller.cc
index 2989579f..b8674bb 100644
--- a/ash/shelf/shelf_party_feature_pod_controller.cc
+++ b/ash/shelf/shelf_party_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/shelf/shelf_party_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -36,7 +37,15 @@
   return button_;
 }
 
+QsFeatureCatalogName ShelfPartyFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kShelfParty;
+}
+
 void ShelfPartyFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(/*target_toggle_state=*/!Shell::Get()
+                     ->shelf_controller()
+                     ->model()
+                     ->in_shelf_party());
   Shell::Get()->shelf_controller()->model()->ToggleShelfParty();
 }
 
diff --git a/ash/shelf/shelf_party_feature_pod_controller.h b/ash/shelf/shelf_party_feature_pod_controller.h
index 76d2a66..0282685 100644
--- a/ash/shelf/shelf_party_feature_pod_controller.h
+++ b/ash/shelf/shelf_party_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SHELF_SHELF_PARTY_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/public/cpp/shelf_model_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
@@ -27,6 +28,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/shelf/window_scale_animation.cc b/ash/shelf/window_scale_animation.cc
index ce6e1ad1..9b94727 100644
--- a/ash/shelf/window_scale_animation.cc
+++ b/ash/shelf/window_scale_animation.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/time/time.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
@@ -100,8 +101,8 @@
  public:
   AnimationObserver(aura::Window* window,
                     WindowScaleAnimation* window_scale_animation)
-      : window_scale_animation_(window_scale_animation) {
-    window_observation_.Observe(window);
+      : window_(window), window_scale_animation_(window_scale_animation) {
+    window_observation_.Observe(window_);
   }
 
   AnimationObserver(const AnimationObserver&) = delete;
@@ -112,6 +113,8 @@
     StopObservingImplicitAnimations();
   }
 
+  aura::Window* window() { return window_; }
+
   // ui::ImplicitAnimationObserver:
   void OnImplicitAnimationsCompleted() override {
     window_scale_animation_->DestroyWindowAnimationObserver(this);
@@ -125,32 +128,33 @@
   }
 
  private:
+  // Pointers to the window and the parent scale animation. Guaranteed to
+  // outlive `this`.
+  aura::Window* const window_;
+
   WindowScaleAnimation* const window_scale_animation_;
 
   base::ScopedObservation<aura::Window, aura::WindowObserver>
       window_observation_{this};
 };
 
-WindowScaleAnimation::WindowScaleAnimation(aura::Window* window,
-                                           WindowScaleType scale_type,
+WindowScaleAnimation::WindowScaleAnimation(WindowScaleType scale_type,
                                            base::OnceClosure opt_callback)
-    : window_(window),
-      opt_callback_(std::move(opt_callback)),
-      scale_type_(scale_type) {}
+    : opt_callback_(std::move(opt_callback)), scale_type_(scale_type) {}
 
 WindowScaleAnimation::~WindowScaleAnimation() {
   if (!opt_callback_.is_null())
     std::move(opt_callback_).Run();
 }
 
-void WindowScaleAnimation::Start() {
+void WindowScaleAnimation::Start(aura::Window* window) {
   // In the destructor of `ScopedLayerAnimationSettings`, it will activate all
   // of its observers. What we want is to activate the observer for each
   // transient child window after the for loop is done, otherwise `this` can be
   // early released via `WindowScaleAnimation::DestroyWindowAnimationObserver`.
   // Hence creating this vector outside of the for loop.
   std::vector<std::unique_ptr<ui::ScopedLayerAnimationSettings>> all_settings;
-  for (auto* transient_window : GetTransientTreeIterator(window_)) {
+  for (auto* transient_window : GetTransientTreeIterator(window)) {
     window_animation_observers_.push_back(
         std::make_unique<AnimationObserver>(transient_window, this));
     WindowBackdrop::Get(transient_window)->DisableBackdrop();
@@ -183,19 +187,22 @@
 
 void WindowScaleAnimation::DestroyWindowAnimationObserver(
     WindowScaleAnimation::AnimationObserver* animation_observer) {
+  // `animation_observer` will get deleted on the next line.
+  auto* window = animation_observer->window();
+
   base::EraseIf(window_animation_observers_,
-                [animation_observer](const auto& observer) {
-                  return observer.get() == animation_observer;
-                });
+                base::MatchesUniquePtr(animation_observer));
+
   if (window_animation_observers_.empty()) {
     // Do the scale transform for the entire transient tree.
-    OnScaleWindowsOnAnimationsCompleted();
-    // self-destructed when all windows' transform animation is done.
+    OnScaleWindowsOnAnimationsCompleted(window);
+    // self-destructed when all windows' transform animation is done.
     delete this;
   }
 }
 
-void WindowScaleAnimation::OnScaleWindowsOnAnimationsCompleted() {
+void WindowScaleAnimation::OnScaleWindowsOnAnimationsCompleted(
+    aura::Window* window) {
   // Scale-down or scale-up window(s) with the windows' descending order
   // in the transient tree. We need to use this fixed order to ensure the
   // transient child window will be visible after returning back from home
@@ -205,16 +212,19 @@
   // details.
   const bool is_scaling_down =
       scale_type_ == WindowScaleAnimation::WindowScaleType::kScaleDownToShelf;
-  for (auto* window : GetTransientTreeIterator(window_)) {
+  for (auto* transient_window : GetTransientTreeIterator(window)) {
+    if (transient_window->is_destroying())
+      continue;
+
     if (is_scaling_down) {
-      // Minimize the dragged window after transform animation iscompleted.
-      window_util::MinimizeAndHideWithoutAnimation({window});
+      // Minimize the dragged window after transform animation is completed.
+      window_util::MinimizeAndHideWithoutAnimation({transient_window});
       // Reset its transform to identity transform and its original backdrop
       // mode.
-      window->layer()->SetTransform(gfx::Transform());
-      window->layer()->SetOpacity(1.f);
+      transient_window->layer()->SetTransform(gfx::Transform());
+      transient_window->layer()->SetOpacity(1.f);
     }
-    WindowBackdrop::Get(window)->RestoreBackdrop();
+    WindowBackdrop::Get(transient_window)->RestoreBackdrop();
   }
 }
 
diff --git a/ash/shelf/window_scale_animation.h b/ash/shelf/window_scale_animation.h
index c3f332eb..f83998dd 100644
--- a/ash/shelf/window_scale_animation.h
+++ b/ash/shelf/window_scale_animation.h
@@ -31,8 +31,7 @@
     kScaleUpToRestore,
   };
 
-  WindowScaleAnimation(aura::Window* window,
-                       WindowScaleType scale_type,
+  WindowScaleAnimation(WindowScaleType scale_type,
                        base::OnceClosure opt_callback);
 
   WindowScaleAnimation(const WindowScaleAnimation&) = delete;
@@ -41,8 +40,8 @@
   ~WindowScaleAnimation();
 
   // Starts animating and creating animation observers for all window(s) in the
-  // transient tree of `window_` in a descending order,
-  void Start();
+  // transient tree of `window` in a descending order,
+  void Start(aura::Window* window);
 
   // For tests only:
   static base::AutoReset<bool>
@@ -54,14 +53,15 @@
   void DestroyWindowAnimationObserver(
       WindowScaleAnimation::AnimationObserver* animation_observer);
 
-  void OnScaleWindowsOnAnimationsCompleted();
+  // `window` is the last window in the transient tree to complete its
+  // animation.
+  void OnScaleWindowsOnAnimationsCompleted(aura::Window* window);
 
-  aura::Window* window_;
   base::OnceClosure opt_callback_;
 
   const WindowScaleType scale_type_;
 
-  // Each window in the transient tree has its own |WindowAnimationObserver|.
+  // Each window in the transient tree has its own `WindowAnimationObserver`.
   std::vector<std::unique_ptr<AnimationObserver>> window_animation_observers_;
 };
 
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller.cc b/ash/system/accessibility/accessibility_feature_pod_controller.cc
index 93d57f1b..6e429d06 100644
--- a/ash/system/accessibility/accessibility_feature_pod_controller.cc
+++ b/ash/system/accessibility/accessibility_feature_pod_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/accessibility/accessibility_feature_pod_controller.h"
 
 #include "ash/accessibility/accessibility_delegate.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -42,7 +43,12 @@
   return button;
 }
 
+QsFeatureCatalogName AccessibilityFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kAccessibility;
+}
+
 void AccessibilityFeaturePodController::OnIconPressed() {
+  TrackDiveInUMA();
   tray_controller_->ShowAccessibilityDetailedView();
 }
 
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller.h b/ash/system/accessibility/accessibility_feature_pod_controller.h
index 0aade23b..d9a7a72 100644
--- a/ash/system/accessibility/accessibility_feature_pod_controller.h
+++ b/ash/system/accessibility/accessibility_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_ACCESSIBILITY_ACCESSIBILITY_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
 namespace ash {
@@ -28,6 +29,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc b/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
index faa8afe1..9a44a41 100644
--- a/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
+++ b/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
@@ -4,11 +4,13 @@
 
 #include "ash/system/accessibility/accessibility_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
-#include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
 
 namespace ash {
 
@@ -26,17 +28,12 @@
 
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
-
-    tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    tray_controller_ =
-        std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
+    GetPrimaryUnifiedSystemTray()->ShowBubble();
   }
 
   void TearDown() override {
     button_.reset();
     controller_.reset();
-    tray_controller_.reset();
-    tray_model_.reset();
     NoSessionAshTestBase::TearDown();
   }
 
@@ -48,14 +45,18 @@
   }
 
   UnifiedSystemTrayController* tray_controller() {
-    return tray_controller_.get();
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_system_tray_controller();
   }
 
   FeaturePodButton* button() { return button_.get(); }
 
+  void PressIcon() { controller_->OnIconPressed(); }
+
+  void PressLabel() { controller_->OnLabelPressed(); }
+
  private:
-  scoped_refptr<UnifiedSystemTrayModel> tray_model_;
-  std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
   std::unique_ptr<AccessibilityFeaturePodController> controller_;
   std::unique_ptr<FeaturePodButton> button_;
 };
@@ -73,4 +74,62 @@
   EXPECT_FALSE(button()->GetVisible());
 }
 
+TEST_F(AccessibilityFeaturePodControllerTest, IconUMATracking) {
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show a11y detailed view when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kAccessibility,
+                                      /*expected_count=*/1);
+}
+
+TEST_F(AccessibilityFeaturePodControllerTest, LabelUMATracking) {
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show a11y detailed view when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kAccessibility,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller.cc b/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
index 9ddc50cc..c745431 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/bluetooth/bluetooth_feature_pod_controller.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/bluetooth_config_service.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -45,18 +46,31 @@
   return button_;
 }
 
+QsFeatureCatalogName BluetoothFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kBluetooth;
+}
+
 void BluetoothFeaturePodController::OnIconPressed() {
   if (!button_->GetEnabled())
     return;
+
   const bool is_toggled = button_->IsToggled();
   remote_cros_bluetooth_config_->SetBluetoothEnabledState(!is_toggled);
-  if (!is_toggled)
-    tray_controller_->ShowBluetoothDetailedView();
+
+  if (is_toggled) {
+    TrackToggleUMA(/*target_toggle_state=*/false);
+    return;
+  }
+
+  TrackDiveInUMA();
+  tray_controller_->ShowBluetoothDetailedView();
 }
 
 void BluetoothFeaturePodController::OnLabelPressed() {
   if (!button_->GetEnabled())
     return;
+
+  TrackDiveInUMA();
   if (!button_->IsToggled())
     remote_cros_bluetooth_config_->SetBluetoothEnabledState(true);
   tray_controller_->ShowBluetoothDetailedView();
@@ -110,9 +124,9 @@
   // them over the default battery in order to match the Quick Settings
   // Bluetooth sub-page battery details shown. Android only shows the left bud
   // if there are multiple batteries, so we match that here, and if it doesn't
-  // exist, we prioritize the right bud battery, then the case battery, if they
-  // exist over the default battery in order to match any detailed battery
-  // shown on the sub-page.
+  // exist, we prioritize the right bud battery, then the case battery, if
+  // they exist over the default battery in order to match any detailed
+  // battery shown on the sub-page.
   if (first_connected_device_.value().battery_info->left_bud_info)
     return first_connected_device_.value()
         .battery_info->left_bud_info->battery_percentage;
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller.h b/ash/system/bluetooth/bluetooth_feature_pod_controller.h
index 0f3a733e..dc001d1 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller.h
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/tray/system_tray_item_uma_type.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 #include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
@@ -36,6 +37,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.cc b/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.cc
index b5298282..c2910f8 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.cc
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -43,16 +44,26 @@
   return button_;
 }
 
+QsFeatureCatalogName BluetoothFeaturePodControllerLegacy::GetCatalogName() {
+  return QsFeatureCatalogName::kBluetooth;
+}
+
 void BluetoothFeaturePodControllerLegacy::OnIconPressed() {
   bool was_enabled = button_->IsToggled();
   Shell::Get()->tray_bluetooth_helper()->SetBluetoothEnabled(!was_enabled);
 
+  if (was_enabled) {
+    TrackToggleUMA(/*target_toggle_state=*/false);
+    return;
+  }
+
   // If Bluetooth was disabled, show device list as well as enabling Bluetooth.
-  if (!was_enabled)
-    tray_controller_->ShowBluetoothDetailedView();
+  TrackDiveInUMA();
+  tray_controller_->ShowBluetoothDetailedView();
 }
 
 void BluetoothFeaturePodControllerLegacy::OnLabelPressed() {
+  TrackDiveInUMA();
   Shell::Get()->tray_bluetooth_helper()->SetBluetoothEnabled(true);
   tray_controller_->ShowBluetoothDetailedView();
 }
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.h b/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.h
index 4168cdf..dc424ed 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.h
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller_legacy.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -33,6 +34,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc b/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
index a4abb13..bddc2de 100644
--- a/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_feature_pod_controller_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/unified/detailed_view_controller.h"
@@ -22,6 +23,7 @@
 #include "base/i18n/number_formatting.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/ash/services/bluetooth_config/fake_device_cache.h"
@@ -447,4 +449,74 @@
   EXPECT_FALSE(feature_pod_button_->GetEnabled());
 }
 
+TEST_F(BluetoothFeaturePodControllerTest, IconUMATracking) {
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Disable bluetooth when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kBluetooth,
+      /*expected_count=*/1);
+
+  // Go to the bluetooth detailed page when pressing on the icon again.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kBluetooth,
+      /*expected_count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kBluetooth,
+                                      /*expected_count=*/1);
+}
+
+TEST_F(BluetoothFeaturePodControllerTest, LabelUMATracking) {
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show bluetooth detailed view when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kBluetooth,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/camera/autozoom_feature_pod_controller.cc b/ash/system/camera/autozoom_feature_pod_controller.cc
index b4480a20..cc45653 100644
--- a/ash/system/camera/autozoom_feature_pod_controller.cc
+++ b/ash/system/camera/autozoom_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/camera/autozoom_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -40,15 +41,18 @@
   return button_;
 }
 
+QsFeatureCatalogName AutozoomFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kAutozoom;
+}
+
 SystemTrayItemUmaType AutozoomFeaturePodController::GetUmaType() const {
   return SystemTrayItemUmaType::UMA_AUTOZOOM;
 }
 
-void AutozoomFeaturePodController::OnLabelPressed() {
-  Shell::Get()->autozoom_controller()->Toggle();
-}
-
 void AutozoomFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(
+      /*target_toggle_state=*/Shell::Get()->autozoom_controller()->GetState() !=
+      cros::mojom::CameraAutoFramingState::ON_SINGLE);
   Shell::Get()->autozoom_controller()->Toggle();
 }
 
diff --git a/ash/system/camera/autozoom_feature_pod_controller.h b/ash/system/camera/autozoom_feature_pod_controller.h
index 5840fc67..013bd1d 100644
--- a/ash/system/camera/autozoom_feature_pod_controller.h
+++ b/ash/system/camera/autozoom_feature_pod_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_CAMERA_AUTOZOOM_FEATURE_POD_CONTROLLER_H_
 #define ASH_SYSTEM_CAMERA_AUTOZOOM_FEATURE_POD_CONTROLLER_H_
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/camera/autozoom_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -24,8 +25,8 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
-  void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
   // AutozoomObserver:
diff --git a/ash/system/cast/cast_feature_pod_controller.cc b/ash/system/cast/cast_feature_pod_controller.cc
index 389b5e7..663552de 100644
--- a/ash/system/cast/cast_feature_pod_controller.cc
+++ b/ash/system/cast/cast_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/cast/cast_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -19,8 +20,7 @@
 
 CastFeaturePodController::CastFeaturePodController(
     UnifiedSystemTrayController* tray_controller)
-    : tray_controller_(tray_controller) {
-}
+    : tray_controller_(tray_controller) {}
 
 CastFeaturePodController::~CastFeaturePodController() {
   if (CastConfigController::Get() && button_)
@@ -46,6 +46,10 @@
   return button_;
 }
 
+QsFeatureCatalogName CastFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kCast;
+}
+
 void CastFeaturePodController::OnIconPressed() {
   auto* cast_config = CastConfigController::Get();
   // If there are no devices currently available for the user, and they have
@@ -54,14 +58,19 @@
   // casting immediately.
   if (cast_config && !cast_config->HasSinksAndRoutes() &&
       cast_config->AccessCodeCastingEnabled()) {
+    TrackToggleUMA(/*target_toggle_state=*/true);
+
     Shell::Get()->system_tray_model()->client()->ShowAccessCodeCastingDialog(
         AccessCodeCastDialogOpenLocation::kSystemTrayCastFeaturePod);
   } else {
+    TrackDiveInUMA();
     tray_controller_->ShowCastDetailedView();
   }
 }
 
 void CastFeaturePodController::OnLabelPressed() {
+  TrackDiveInUMA();
+
   // Clicking on the label should always launch the full UI.
   tray_controller_->ShowCastDetailedView();
 }
diff --git a/ash/system/cast/cast_feature_pod_controller.h b/ash/system/cast/cast_feature_pod_controller.h
index 8a91e337..a18cfae 100644
--- a/ash/system/cast/cast_feature_pod_controller.h
+++ b/ash/system/cast/cast_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_CAST_CAST_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/cast_config_controller.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -28,6 +29,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
index 6397377..d12a192 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -48,12 +49,18 @@
   return button_;
 }
 
+QsFeatureCatalogName DarkModeFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kDarkMode;
+}
+
 void DarkModeFeaturePodController::OnIconPressed() {
   // Toggling Dark theme feature pod button inside quick settings should cancel
   // auto scheduling. This ensures that on and off states of the pod button
   // match the non-scheduled states of Dark and Light buttons in
   // personalization hub respectively.
   auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
+  TrackToggleUMA(
+      /*target_toggle_state=*/!dark_light_mode_controller->IsDarkModeEnabled());
   dark_light_mode_controller->SetAutoScheduleEnabled(
       /*enabled=*/false);
   dark_light_mode_controller->ToggleColorMode();
@@ -62,6 +69,7 @@
 }
 
 void DarkModeFeaturePodController::OnLabelPressed() {
+  TrackDiveInUMA();
   if (ash::features::IsPersonalizationHubEnabled())
     Shell::Get()->system_tray_model()->client()->ShowDarkModeSettings();
 }
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller.h b/ash/system/dark_mode/dark_mode_feature_pod_controller.h
index dad58c3..150eb1b 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller.h
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_DARK_MODE_DARK_MODE_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/style/color_mode_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -27,6 +28,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc b/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc
index a3b371f..f653d4e1 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/dark_light_mode_controller_impl.h"
@@ -11,6 +12,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/test/ash_test_base.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 
@@ -38,6 +40,17 @@
   std::unique_ptr<FeaturePodButton> button(
       dark_mode_feature_pod_controller->CreateButton());
 
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
   // Enable dark mode auto scheduling.
   auto* controller = Shell::Get()->dark_light_mode_controller();
   controller->SetAutoScheduleEnabled(true);
@@ -47,20 +60,61 @@
   bool dark_mode_enabled = dark_light_mode_controller->IsDarkModeEnabled();
   EXPECT_EQ(dark_mode_enabled, button->IsToggled());
 
+  // Set the init state to enabled.
+  if (!dark_mode_enabled)
+    dark_light_mode_controller->ToggleColorMode();
+
   // Pressing the dark mode button should disable the scheduling and switch the
   // dark mode status.
   dark_mode_feature_pod_controller->OnIconPressed();
   EXPECT_FALSE(controller->GetAutoScheduleEnabled());
-  EXPECT_EQ(!dark_mode_enabled,
-            dark_light_mode_controller->IsDarkModeEnabled());
-  EXPECT_EQ(!dark_mode_enabled, button->IsToggled());
+  EXPECT_EQ(false, dark_light_mode_controller->IsDarkModeEnabled());
+  EXPECT_EQ(false, button->IsToggled());
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kDarkMode,
+      /*expected_count=*/1);
 
   // Pressing the dark mode button again should only switch the dark mode status
   // while maintaining the disabled status of scheduling.
   dark_mode_feature_pod_controller->OnIconPressed();
   EXPECT_FALSE(controller->GetAutoScheduleEnabled());
-  EXPECT_EQ(dark_mode_enabled, dark_light_mode_controller->IsDarkModeEnabled());
-  EXPECT_EQ(dark_mode_enabled, button->IsToggled());
+  EXPECT_EQ(true, dark_light_mode_controller->IsDarkModeEnabled());
+  EXPECT_EQ(true, button->IsToggled());
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kDarkMode,
+      /*expected_count=*/1);
+
+  dark_mode_feature_pod_controller->OnLabelPressed();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kDarkMode,
+                                      /*expected_count=*/1);
+
   system_tray->CloseBubble();
 }
 
diff --git a/ash/system/holding_space/downloads_section.cc b/ash/system/holding_space/downloads_section.cc
index c9be161..e4eb59d 100644
--- a/ash/system/holding_space/downloads_section.cc
+++ b/ash/system/holding_space/downloads_section.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
 #include "ash/public/cpp/holding_space/holding_space_metrics.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -111,8 +112,7 @@
 
 DownloadsSection::DownloadsSection(HoldingSpaceViewDelegate* delegate)
     : HoldingSpaceItemViewsSection(delegate,
-                                   holding_space_util::DownloadSupportedTypes(),
-                                   /*max_count=*/kMaxDownloads) {}
+                                   HoldingSpaceSectionId::kDownloads) {}
 
 DownloadsSection::~DownloadsSection() = default;
 
diff --git a/ash/system/holding_space/holding_space_item_views_section.cc b/ash/system/holding_space/holding_space_item_views_section.cc
index 599d652..75ad793 100644
--- a/ash/system/holding_space/holding_space_item_views_section.cc
+++ b/ash/system/holding_space/holding_space_item_views_section.cc
@@ -121,11 +121,10 @@
 
 HoldingSpaceItemViewsSection::HoldingSpaceItemViewsSection(
     HoldingSpaceViewDelegate* delegate,
-    std::set<HoldingSpaceItem::Type> supported_types,
-    const absl::optional<size_t>& max_count)
-    : delegate_(delegate),
-      supported_types_(std::move(supported_types)),
-      max_count_(max_count) {}
+    HoldingSpaceSectionId section_id)
+    : delegate_(delegate), section_(GetHoldingSpaceSection(section_id)) {
+  DCHECK(section_);
+}
 
 HoldingSpaceItemViewsSection::~HoldingSpaceItemViewsSection() = default;
 
@@ -147,11 +146,11 @@
   header_->SetVisible(false);
 
   // Container.
-  // NOTE: If `max_count_` is not present `container_` does not limit the number
-  // of holding space item views visible to the user at one time. In this case
-  // `container_` needs to be wrapped in a `views::ScrollView` to allow the user
-  // access to all contained item views.
-  if (max_count_.has_value()) {
+  // NOTE: If `max_visible_item_count` is not present `container_` does not
+  // limit the number of holding space item views visible to the user at one
+  // time. In this case `container_` needs to be wrapped in a
+  // `views::ScrollView` to allow the user access to all contained item views.
+  if (section_->max_visible_item_count.has_value()) {
     container_ = AddChildView(CreateContainer());
   } else {
     auto* scroll = AddChildView(std::make_unique<HoldingSpaceScrollView>());
@@ -282,7 +281,7 @@
   const bool needs_update =
       base::ranges::any_of(items, [this](const HoldingSpaceItem* item) {
         return item->IsInitialized() &&
-               base::Contains(supported_types_, item->type());
+               base::Contains(section_->supported_types, item->type());
       });
   if (needs_update)
     MaybeAnimateOut();
@@ -300,7 +299,7 @@
 
 void HoldingSpaceItemViewsSection::OnHoldingSpaceItemInitialized(
     const HoldingSpaceItem* item) {
-  if (base::Contains(supported_types_, item->type()))
+  if (base::Contains(section_->supported_types, item->type()))
     MaybeAnimateOut();
 }
 
@@ -425,7 +424,8 @@
     HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
     if (model) {
       animate_out_header = base::ranges::none_of(
-          supported_types_, [&model](HoldingSpaceItem::Type supported_type) {
+          section_->supported_types,
+          [&model](HoldingSpaceItem::Type supported_type) {
             return model->ContainsInitializedItemOfType(supported_type);
           });
     }
@@ -503,13 +503,16 @@
   if (!model)
     return;
 
+  const absl::optional<size_t>& max_visible_item_count =
+      section_->max_visible_item_count;
+
   for (const auto& item : model->items()) {
     if (item->IsInitialized() &&
-        base::Contains(supported_types_, item->type())) {
+        base::Contains(section_->supported_types, item->type())) {
       DCHECK(!base::Contains(views_by_item_id_, item->id()));
 
       // Remove the last holding space item view if already at max capacity.
-      if (max_count_ && container_->children().size() == max_count_.value()) {
+      if (max_visible_item_count == container_->children().size()) {
         auto view = container_->RemoveChildViewT(container_->children().back());
         views_by_item_id_.erase(
             HoldingSpaceItemView::Cast(view.get())->item()->id());
diff --git a/ash/system/holding_space/holding_space_item_views_section.h b/ash/system/holding_space/holding_space_item_views_section.h
index 0d844063..6b6c293 100644
--- a/ash/system/holding_space/holding_space_item_views_section.h
+++ b/ash/system/holding_space/holding_space_item_views_section.h
@@ -12,6 +12,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/views/view.h"
 
@@ -33,8 +34,7 @@
 class ASH_EXPORT HoldingSpaceItemViewsSection : public views::View {
  public:
   HoldingSpaceItemViewsSection(HoldingSpaceViewDelegate* delegate,
-                               std::set<HoldingSpaceItem::Type> supported_types,
-                               const absl::optional<size_t>& max_count);
+                               HoldingSpaceSectionId section_id);
   HoldingSpaceItemViewsSection(const HoldingSpaceItemViewsSection& other) =
       delete;
   HoldingSpaceItemViewsSection& operator=(
@@ -79,7 +79,7 @@
 
   // Returns the types of holding space items supported by this section.
   const std::set<HoldingSpaceItem::Type>& supported_types() const {
-    return supported_types_;
+    return section_->supported_types;
   }
 
  protected:
@@ -142,8 +142,7 @@
   void OnAnimateOutCompleted(const ui::CallbackLayerAnimationObserver&);
 
   HoldingSpaceViewDelegate* const delegate_;
-  const std::set<HoldingSpaceItem::Type> supported_types_;
-  const absl::optional<size_t> max_count_;
+  const HoldingSpaceSection* const section_;
 
   // Owned by view hierarchy.
   views::View* header_ = nullptr;
diff --git a/ash/system/holding_space/holding_space_tray_child_bubble_unittest.cc b/ash/system/holding_space/holding_space_tray_child_bubble_unittest.cc
index 1e99f70..60573ca27 100644
--- a/ash/system/holding_space/holding_space_tray_child_bubble_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_child_bubble_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/system/holding_space/holding_space_ash_test_base.h"
 #include "ash/system/holding_space/holding_space_item_chip_view.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
@@ -26,16 +27,9 @@
 
 class TestHoldingSpaceItemViewsSection : public HoldingSpaceItemViewsSection {
  public:
-  struct Params {
-    std::set<HoldingSpaceItem::Type> supported_types;
-    absl::optional<size_t> max_count;
-  };
-
   TestHoldingSpaceItemViewsSection(HoldingSpaceViewDelegate* view_delegate,
-                                   Params params)
-      : HoldingSpaceItemViewsSection(view_delegate,
-                                     std::move(params.supported_types),
-                                     params.max_count) {}
+                                   HoldingSpaceSectionId section_id)
+      : HoldingSpaceItemViewsSection(view_delegate, section_id) {}
 
  private:
   // HoldingSpaceItemViewsSection:
@@ -180,11 +174,7 @@
   std::vector<std::unique_ptr<HoldingSpaceItemViewsSection>> CreateSections(
       HoldingSpaceViewDelegate* view_delegate) {
     auto section = std::make_unique<TestHoldingSpaceItemViewsSection>(
-        view_delegate,
-        TestHoldingSpaceItemViewsSection::Params{
-            .supported_types = {HoldingSpaceItem::Type::kPinnedFile},
-            .max_count = 1u,
-        });
+        view_delegate, HoldingSpaceSectionId::kPinnedFiles);
     section_ = section.get();
     std::vector<std::unique_ptr<HoldingSpaceItemViewsSection>> sections;
     sections.push_back(std::move(section));
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 5f72e85a..aec29092 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -17,6 +17,7 @@
 #include "ash/public/cpp/holding_space/holding_space_metrics.h"
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_prefs.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/public/cpp/holding_space/holding_space_test_api.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "ash/public/cpp/holding_space/mock_holding_space_client.h"
@@ -210,6 +211,10 @@
   return ids;
 }
 
+size_t GetMaxVisibleItemCount(HoldingSpaceSectionId section_id) {
+  return GetHoldingSpaceSection(section_id)->max_visible_item_count.value();
+}
+
 // PredicateWaiter -------------------------------------------------------------
 
 // A class capable of waiting until a predicate returns true.
@@ -2454,9 +2459,12 @@
   MarkTimeOfFirstPin();
   StartSession();
 
+  const size_t max_screen_captures =
+      GetMaxVisibleItemCount(HoldingSpaceSectionId::kScreenCaptures);
+
   // Add a number of initialized screen capture items.
   std::deque<HoldingSpaceItem*> items;
-  for (size_t i = 0; i < kMaxScreenCaptures; ++i) {
+  for (size_t i = 0; i < max_screen_captures; ++i) {
     items.push_back(
         AddItem(HoldingSpaceItem::Type::kScreenshot,
                 base::FilePath("/tmp/fake_" + base::NumberToString(i))));
@@ -2899,8 +2907,11 @@
   EXPECT_EQ(items[0]->id(),
             HoldingSpaceItemView::Cast(download_chips[0])->item()->id());
 
+  const size_t max_downloads =
+      GetMaxVisibleItemCount(HoldingSpaceSectionId::kDownloads);
+
   // Add a few more download items until the section reaches capacity.
-  for (size_t i = 2; i <= kMaxDownloads; ++i) {
+  for (size_t i = 2; i <= max_downloads; ++i) {
     items.push_back(AddItem(
         GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
   }
@@ -2909,7 +2920,7 @@
   EXPECT_TRUE(test_api()->GetSuggestionChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(kMaxDownloads, download_chips.size());
+  ASSERT_EQ(max_downloads, download_chips.size());
 
   // All downloads should be visible except for that which is associated with
   // the partially initialized item at index == `1`.
@@ -2947,7 +2958,7 @@
   EXPECT_TRUE(test_api()->GetSuggestionChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(kMaxDownloads, download_chips.size());
+  ASSERT_EQ(max_downloads, download_chips.size());
 
   for (int download_chip_index = 0, item_index = items.size() - 1;
        item_index >= 0; ++download_chip_index, --item_index) {
@@ -2976,9 +2987,12 @@
   MarkTimeOfFirstPin();
   StartSession();
 
+  const size_t max_downloads =
+      GetMaxVisibleItemCount(HoldingSpaceSectionId::kDownloads);
+
   // Add a number of initialized download items.
   std::deque<HoldingSpaceItem*> items;
-  for (size_t i = 0; i < kMaxDownloads; ++i) {
+  for (size_t i = 0; i < max_downloads; ++i) {
     items.push_back(AddItem(
         GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
   }
@@ -3015,8 +3029,11 @@
   items.push_back(
       AddPartiallyInitializedItem(GetType(), base::FilePath("/tmp/fake_1")));
 
+  const size_t max_downloads =
+      GetMaxVisibleItemCount(HoldingSpaceSectionId::kDownloads);
+
   // Add download items until the section reaches capacity.
-  for (size_t i = 1; i < kMaxDownloads + 1; ++i) {
+  for (size_t i = 1; i < max_downloads + 1; ++i) {
     items.push_back(AddItem(
         GetType(), base::FilePath("/tmp/fake_" + base::NumberToString(i))));
   }
@@ -3025,7 +3042,7 @@
   EXPECT_TRUE(test_api()->GetSuggestionChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   std::vector<views::View*> download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(kMaxDownloads, download_chips.size());
+  ASSERT_EQ(max_downloads, download_chips.size());
 
   for (size_t download_chip_index = 0, item_index = items.size() - 1;
        item_index > 0; ++download_chip_index, --item_index) {
@@ -3042,7 +3059,7 @@
   EXPECT_TRUE(test_api()->GetSuggestionChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(kMaxDownloads, download_chips.size());
+  ASSERT_EQ(max_downloads, download_chips.size());
 
   for (size_t download_chip_index = 0, item_index = items.size() - 1;
        item_index > 0; ++download_chip_index, --item_index) {
@@ -3059,7 +3076,7 @@
   EXPECT_TRUE(test_api()->GetSuggestionChips().empty());
   EXPECT_TRUE(test_api()->GetScreenCaptureViews().empty());
   download_chips = test_api()->GetDownloadChips();
-  ASSERT_EQ(kMaxDownloads, download_chips.size());
+  ASSERT_EQ(max_downloads, download_chips.size());
 
   for (int download_chip_index = 0, item_index = items.size() - 1;
        item_index >= 0; ++download_chip_index, --item_index) {
diff --git a/ash/system/holding_space/pinned_files_section.cc b/ash/system/holding_space/pinned_files_section.cc
index 531d0df..963f445 100644
--- a/ash/system/holding_space/pinned_files_section.cc
+++ b/ash/system/holding_space/pinned_files_section.cc
@@ -13,6 +13,7 @@
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/holding_space/holding_space_metrics.h"
 #include "ash/public/cpp/holding_space/holding_space_prefs.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -171,9 +172,7 @@
 
 PinnedFilesSection::PinnedFilesSection(HoldingSpaceViewDelegate* delegate)
     : HoldingSpaceItemViewsSection(delegate,
-                                   /*supported_types=*/
-                                   {HoldingSpaceItem::Type::kPinnedFile},
-                                   /*max_count=*/absl::nullopt) {
+                                   HoldingSpaceSectionId::kPinnedFiles) {
   SetID(kHoldingSpacePinnedFilesSectionId);
 }
 
diff --git a/ash/system/holding_space/screen_captures_section.cc b/ash/system/holding_space/screen_captures_section.cc
index d27cdc2..c3f05b0d 100644
--- a/ash/system/holding_space/screen_captures_section.cc
+++ b/ash/system/holding_space/screen_captures_section.cc
@@ -7,6 +7,7 @@
 #include "ash/bubble/bubble_utils.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/holding_space/holding_space_section.h"
 #include "ash/public/cpp/holding_space/holding_space_util.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/holding_space/holding_space_item_screen_capture_view.h"
@@ -20,10 +21,8 @@
 namespace ash {
 
 ScreenCapturesSection::ScreenCapturesSection(HoldingSpaceViewDelegate* delegate)
-    : HoldingSpaceItemViewsSection(
-          delegate,
-          holding_space_util::ScreenCaptureSupportedTypes(),
-          /*max_count=*/kMaxScreenCaptures) {}
+    : HoldingSpaceItemViewsSection(delegate,
+                                   HoldingSpaceSectionId::kScreenCaptures) {}
 
 ScreenCapturesSection::~ScreenCapturesSection() = default;
 
diff --git a/ash/system/holding_space/suggestions_section.cc b/ash/system/holding_space/suggestions_section.cc
index 86115f8..610e55b2 100644
--- a/ash/system/holding_space/suggestions_section.cc
+++ b/ash/system/holding_space/suggestions_section.cc
@@ -107,14 +107,12 @@
 
   void OnPressed() {
     auto* prefs = Shell::Get()->session_controller()->GetActivePrefService();
-    bool expanded_after_toggle =
-        !holding_space_prefs::IsSuggestionsExpanded(prefs);
-    holding_space_prefs::SetSuggestionsExpanded(prefs, expanded_after_toggle);
+    bool expanded = holding_space_prefs::IsSuggestionsExpanded(prefs);
+    holding_space_prefs::SetSuggestionsExpanded(prefs, !expanded);
 
     holding_space_metrics::RecordSuggestionsAction(
-        expanded_after_toggle
-            ? holding_space_metrics::SuggestionsAction::kExpand
-            : holding_space_metrics::SuggestionsAction::kCollapse);
+        expanded ? holding_space_metrics::SuggestionsAction::kCollapse
+                 : holding_space_metrics::SuggestionsAction::kExpand);
   }
 
   // Sets the header's `chevron_` icon to the correct color (based on theme) and
@@ -145,10 +143,7 @@
 
 SuggestionsSection::SuggestionsSection(HoldingSpaceViewDelegate* delegate)
     : HoldingSpaceItemViewsSection(delegate,
-                                   /*supported_types=*/
-                                   {HoldingSpaceItem::Type::kDriveSuggestion,
-                                    HoldingSpaceItem::Type::kLocalSuggestion},
-                                   /*max_count=*/kMaxSuggestions) {
+                                   HoldingSpaceSectionId::kSuggestions) {
   SetID(kHoldingSpaceSuggestionsSectionId);
 
   auto* prefs = Shell::Get()->session_controller()->GetActivePrefService();
diff --git a/ash/system/ime/ime_feature_pod_controller.cc b/ash/system/ime/ime_feature_pod_controller.cc
index b99ea04..f679696 100644
--- a/ash/system/ime/ime_feature_pod_controller.cc
+++ b/ash/system/ime/ime_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/ime/ime_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/keyboard/ui/keyboard_util.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -74,7 +75,12 @@
   return button_;
 }
 
+QsFeatureCatalogName IMEFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kIME;
+}
+
 void IMEFeaturePodController::OnIconPressed() {
+  TrackDiveInUMA();
   tray_controller_->ShowIMEDetailedView();
 }
 
diff --git a/ash/system/ime/ime_feature_pod_controller.h b/ash/system/ime/ime_feature_pod_controller.h
index d040c8f..e97660b 100644
--- a/ash/system/ime/ime_feature_pod_controller.h
+++ b/ash/system/ime/ime_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_IME_IME_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/ime/ime_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -27,6 +28,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/system/ime/ime_feature_pod_controller_unittest.cc b/ash/system/ime/ime_feature_pod_controller_unittest.cc
index 32fd1eae..a9f5f00 100644
--- a/ash/system/ime/ime_feature_pod_controller_unittest.cc
+++ b/ash/system/ime/ime_feature_pod_controller_unittest.cc
@@ -6,15 +6,17 @@
 
 #include <vector>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
-#include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 
 namespace ash {
 
@@ -32,16 +34,12 @@
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
 
-    tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    tray_controller_ =
-        std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
+    GetPrimaryUnifiedSystemTray()->ShowBubble();
   }
 
   void TearDown() override {
     button_.reset();
     controller_.reset();
-    tray_controller_.reset();
-    tray_model_.reset();
     NoSessionAshTestBase::TearDown();
   }
 
@@ -52,7 +50,9 @@
   }
 
   UnifiedSystemTrayController* tray_controller() {
-    return tray_controller_.get();
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_system_tray_controller();
   }
 
   FeaturePodButton* button() { return button_.get(); }
@@ -78,9 +78,11 @@
         current_ime_.id, std::move(available_imes), std::move(menu_items));
   }
 
+  void PressIcon() { controller_->OnIconPressed(); }
+
+  void PressLabel() { controller_->OnLabelPressed(); }
+
  private:
-  scoped_refptr<UnifiedSystemTrayModel> tray_model_;
-  std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
   std::unique_ptr<IMEFeaturePodController> controller_;
   std::unique_ptr<FeaturePodButton> button_;
 
@@ -127,4 +129,62 @@
   EXPECT_TRUE(button()->GetVisible());
 }
 
+TEST_F(IMEFeaturePodControllerTest, IconUMATracking) {
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show IME detailed view when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kIME,
+                                      /*expected_count=*/1);
+}
+
+TEST_F(IMEFeaturePodControllerTest, LabelUMATracking) {
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show IME detailed view when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kIME,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/locale/locale_feature_pod_controller.cc b/ash/system/locale/locale_feature_pod_controller.cc
index 58f91fd..a9c8f4d7 100644
--- a/ash/system/locale/locale_feature_pod_controller.cc
+++ b/ash/system/locale/locale_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/locale/locale_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -44,7 +45,12 @@
   return button;
 }
 
+QsFeatureCatalogName LocaleFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kLocale;
+}
+
 void LocaleFeaturePodController::OnIconPressed() {
+  TrackDiveInUMA();
   tray_controller_->ShowLocaleDetailedView();
 }
 
diff --git a/ash/system/locale/locale_feature_pod_controller.h b/ash/system/locale/locale_feature_pod_controller.h
index 59b7477..fd8b9e3 100644
--- a/ash/system/locale/locale_feature_pod_controller.h
+++ b/ash/system/locale/locale_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_LOCALE_LOCALE_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
 namespace ash {
@@ -26,6 +27,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/system/locale/locale_feature_pod_controller_unittest.cc b/ash/system/locale/locale_feature_pod_controller_unittest.cc
index 8daa176..f4f4c69 100644
--- a/ash/system/locale/locale_feature_pod_controller_unittest.cc
+++ b/ash/system/locale/locale_feature_pod_controller_unittest.cc
@@ -7,15 +7,17 @@
 #include <memory>
 #include <vector>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/locale_update_controller.h"
 #include "ash/shell.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
-#include "base/memory/scoped_refptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 
 namespace ash {
 namespace {
@@ -33,48 +35,119 @@
 
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
-
-    tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    tray_controller_ =
-        std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
-    controller_ =
-        std::make_unique<LocaleFeaturePodController>(tray_controller_.get());
+    GetPrimaryUnifiedSystemTray()->ShowBubble();
   }
 
   void TearDown() override {
+    button_.reset();
     controller_.reset();
-    tray_controller_.reset();
-    tray_model_.reset();
     NoSessionAshTestBase::TearDown();
   }
 
- protected:
-  std::unique_ptr<LocaleFeaturePodController> controller_;
+  void SetUpButton() {
+    controller_ =
+        std::make_unique<LocaleFeaturePodController>(tray_controller());
+    button_.reset(controller_->CreateButton());
+  }
+
+  UnifiedSystemTrayController* tray_controller() {
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_system_tray_controller();
+  }
+
+  FeaturePodButton* button() { return button_.get(); }
+
+  void PressIcon() { controller_->OnIconPressed(); }
+
+  void PressLabel() { controller_->OnLabelPressed(); }
 
  private:
-  scoped_refptr<UnifiedSystemTrayModel> tray_model_;
-  std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
+  std::unique_ptr<LocaleFeaturePodController> controller_;
+  std::unique_ptr<FeaturePodButton> button_;
 };
 
 TEST_F(LocaleFeaturePodControllerTest, ButtonVisibility) {
   constexpr char kDefaultLocaleIsoCode[] = "en-US";
   // The button is invisible if the locale list is unset.
-  std::unique_ptr<FeaturePodButton> button;
-  button.reset(controller_->CreateButton());
-  EXPECT_FALSE(button->GetVisible());
+  SetUpButton();
+  EXPECT_FALSE(button()->GetVisible());
 
   // The button is invisible if the locale list is empty.
   Shell::Get()->system_tray_model()->SetLocaleList({}, kDefaultLocaleIsoCode);
-  button.reset(controller_->CreateButton());
-  EXPECT_FALSE(button->GetVisible());
+  SetUpButton();
+  EXPECT_FALSE(button()->GetVisible());
 
   // The button is visible if the locale list is non-empty.
   std::vector<LocaleInfo> locale_list;
   locale_list.emplace_back(kDefaultLocaleIsoCode, u"English (United States)");
   Shell::Get()->system_tray_model()->SetLocaleList(std::move(locale_list),
                                                    kDefaultLocaleIsoCode);
-  button.reset(controller_->CreateButton());
-  EXPECT_TRUE(button->GetVisible());
+  SetUpButton();
+  EXPECT_TRUE(button()->GetVisible());
+}
+
+TEST_F(LocaleFeaturePodControllerTest, IconUMATracking) {
+  std::vector<LocaleInfo> locale_list;
+  constexpr char kDefaultLocaleIsoCode[] = "en-US";
+  locale_list.emplace_back(kDefaultLocaleIsoCode, u"English (United States)");
+  Shell::Get()->system_tray_model()->SetLocaleList(std::move(locale_list),
+                                                   kDefaultLocaleIsoCode);
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show Locale detailed view when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kLocale,
+                                      /*expected_count=*/1);
+}
+
+TEST_F(LocaleFeaturePodControllerTest, LabelUMATracking) {
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show Locale detailed view when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kLocale,
+                                      /*expected_count=*/1);
 }
 
 }  // namespace
diff --git a/ash/system/nearby_share/nearby_share_feature_pod_controller.cc b/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
index 8cddb65..5acf3538 100644
--- a/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
+++ b/ash/system/nearby_share/nearby_share_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/nearby_share/nearby_share_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/nearby_share_delegate.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -80,7 +81,13 @@
   return button_;
 }
 
+QsFeatureCatalogName NearbyShareFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kNearbyShare;
+}
+
 void NearbyShareFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(
+      /*target_toggle_state=*/!nearby_share_delegate_->IsHighVisibilityOn());
   if (nearby_share_delegate_->IsHighVisibilityOn()) {
     nearby_share_delegate_->DisableHighVisibility();
   } else {
@@ -89,6 +96,7 @@
 }
 
 void NearbyShareFeaturePodController::OnLabelPressed() {
+  TrackDiveInUMA();
   nearby_share_delegate_->ShowNearbyShareSettings();
 }
 
diff --git a/ash/system/nearby_share/nearby_share_feature_pod_controller.h b/ash/system/nearby_share/nearby_share_feature_pod_controller.h
index b03c392d..7d4c47f 100644
--- a/ash/system/nearby_share/nearby_share_feature_pod_controller.h
+++ b/ash/system/nearby_share/nearby_share_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_NEARBY_SHARE_NEARBY_SHARE_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/nearby_share/nearby_share_controller_impl.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 #include "base/time/time.h"
@@ -32,6 +33,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/nearby_share/nearby_share_feature_pod_controller_unittest.cc b/ash/system/nearby_share/nearby_share_feature_pod_controller_unittest.cc
index 14f867dd..7466e0656 100644
--- a/ash/system/nearby_share/nearby_share_feature_pod_controller_unittest.cc
+++ b/ash/system/nearby_share/nearby_share_feature_pod_controller_unittest.cc
@@ -4,13 +4,16 @@
 
 #include "ash/system/nearby_share/nearby_share_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/test/test_nearby_share_delegate.h"
 #include "ash/shell.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
 
 namespace ash {
 
@@ -27,34 +30,40 @@
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
 
-    tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    tray_controller_ =
-        std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
-
     test_delegate_ = static_cast<TestNearbyShareDelegate*>(
         Shell::Get()->nearby_share_delegate());
     nearby_share_controller_ = Shell::Get()->nearby_share_controller();
 
     test_delegate_->set_is_pod_button_visible(true);
+
+    GetPrimaryUnifiedSystemTray()->ShowBubble();
   }
 
   void TearDown() override {
     button_.reset();
     pod_controller_.reset();
-    tray_controller_.reset();
-    tray_model_.reset();
     NoSessionAshTestBase::TearDown();
   }
 
  protected:
   void SetUpButton() {
-    pod_controller_ = std::make_unique<NearbyShareFeaturePodController>(
-        tray_controller_.get());
+    pod_controller_ =
+        std::make_unique<NearbyShareFeaturePodController>(tray_controller());
     button_.reset(pod_controller_->CreateButton());
   }
 
-  scoped_refptr<UnifiedSystemTrayModel> tray_model_;
-  std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
+  UnifiedSystemTrayController* tray_controller() {
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_system_tray_controller();
+  }
+
+  FeaturePodButton* button() { return button_.get(); }
+
+  void PressIcon() { pod_controller_->OnIconPressed(); }
+
+  void PressLabel() { pod_controller_->OnLabelPressed(); }
+
   std::unique_ptr<NearbyShareFeaturePodController> pod_controller_;
   std::unique_ptr<FeaturePodButton> button_;
 
@@ -65,14 +74,14 @@
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonVisibilityNotLoggedIn) {
   SetUpButton();
   // If not logged in, it should not be visible.
-  EXPECT_FALSE(button_->GetVisible());
+  EXPECT_FALSE(button()->GetVisible());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonVisibilityLoggedIn) {
   CreateUserSessions(1);
   SetUpButton();
   // If logged in, it should be visible.
-  EXPECT_TRUE(button_->GetVisible());
+  EXPECT_TRUE(button()->GetVisible());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonVisibilityLocked) {
@@ -80,7 +89,7 @@
   BlockUserSession(UserSessionBlockReason::BLOCKED_BY_LOCK_SCREEN);
   SetUpButton();
   // If locked, it should not be visible.
-  EXPECT_FALSE(button_->GetVisible());
+  EXPECT_FALSE(button()->GetVisible());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonVisibilityLoginScreen) {
@@ -89,7 +98,7 @@
   SetUpButton();
   // If the login screen is showing (e.g. multi-user signin), it should not be
   // visible, regardless of whether an active user is signed in.
-  EXPECT_FALSE(button_->GetVisible());
+  EXPECT_FALSE(button()->GetVisible());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonVisiblilityHiddenByDelegate) {
@@ -98,18 +107,18 @@
   SetUpButton();
   // If NearbyShareDelegate::IsPodButtonVisible() returns false, it should
   // not be visible.
-  EXPECT_FALSE(button_->GetVisible());
+  EXPECT_FALSE(button()->GetVisible());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest,
        ButtonToggledByHighVisibilityEnabledEvent) {
   CreateUserSessions(1);
   SetUpButton();
-  ASSERT_FALSE(button_->IsToggled());
+  ASSERT_FALSE(button()->IsToggled());
   nearby_share_controller_->HighVisibilityEnabledChanged(true);
-  EXPECT_TRUE(button_->IsToggled());
+  EXPECT_TRUE(button()->IsToggled());
   nearby_share_controller_->HighVisibilityEnabledChanged(false);
-  EXPECT_FALSE(button_->IsToggled());
+  EXPECT_FALSE(button()->IsToggled());
 }
 
 TEST_F(NearbyShareFeaturePodControllerTest, ButtonPressTogglesHighVisibility) {
@@ -130,4 +139,66 @@
             test_delegate_->method_calls()[1]);
 }
 
+TEST_F(NearbyShareFeaturePodControllerTest, IconUMATracking) {
+  CreateUserSessions(1);
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Toggle on nearby share feature when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kNearbyShare,
+      /*expected_count=*/1);
+}
+
+TEST_F(NearbyShareFeaturePodControllerTest, LabelUMATracking) {
+  CreateUserSessions(1);
+  SetUpButton();
+  nearby_share_controller_->HighVisibilityEnabledChanged(true);
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show nearby share detailed view (setting) when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kNearbyShare,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/network/network_feature_pod_controller.cc b/ash/system/network/network_feature_pod_controller.cc
index 3ae5911f..71e43ad 100644
--- a/ash/system/network/network_feature_pod_controller.cc
+++ b/ash/system/network/network_feature_pod_controller.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -17,6 +18,7 @@
 #include "ash/system/network/network_icon_animation.h"
 #include "ash/system/network/tray_network_state_model.h"
 #include "ash/system/tray/system_tray_notifier.h"
+#include "ash/system/unified/quick_settings_metrics_util.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "base/check.h"
 #include "base/notreached.h"
@@ -180,17 +182,27 @@
   return button.release();
 }
 
+QsFeatureCatalogName NetworkFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kNetwork;
+}
+
 void NetworkFeaturePodController::OnIconPressed() {
   bool was_enabled = button_->IsToggled();
   bool can_toggle = SetNetworkTypeEnabled(!was_enabled);
+  if (can_toggle)
+    TrackToggleUMA(/*target_toggle_state=*/!was_enabled);
 
   // The detailed view should be shown when we enable a network technology as
   // well as when the network technology cannot be toggled, e.g. ethernet.
-  if (!was_enabled || !can_toggle)
+  if (!was_enabled || !can_toggle) {
+    TrackDiveInUMA();
     tray_controller_->ShowNetworkDetailedView(/*force=*/!can_toggle);
+  }
 }
 
 void NetworkFeaturePodController::OnLabelPressed() {
+  TrackDiveInUMA();
+
   SetNetworkTypeEnabled(true);
   tray_controller_->ShowNetworkDetailedView(/*force=*/true);
 }
diff --git a/ash/system/network/network_feature_pod_controller.h b/ash/system/network/network_feature_pod_controller.h
index f5449af..fdc40332 100644
--- a/ash/system/network/network_feature_pod_controller.h
+++ b/ash/system/network/network_feature_pod_controller.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/network/network_feature_pod_button.h"
 #include "ash/system/network/network_icon_animation_observer.h"
 #include "ash/system/network/tray_network_state_observer.h"
@@ -37,6 +38,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/network/network_feature_pod_controller_legacy.cc b/ash/system/network/network_feature_pod_controller_legacy.cc
index 6c80f7d..39cec90 100644
--- a/ash/system/network/network_feature_pod_controller_legacy.cc
+++ b/ash/system/network/network_feature_pod_controller_legacy.cc
@@ -5,6 +5,7 @@
 #include "ash/system/network/network_feature_pod_controller_legacy.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -12,6 +13,8 @@
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/network_feature_pod_button_legacy.h"
 #include "ash/system/network/tray_network_state_model.h"
+#include "ash/system/unified/feature_pod_controller_base.h"
+#include "ash/system/unified/quick_settings_metrics_util.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -63,17 +66,26 @@
   return button_;
 }
 
+QsFeatureCatalogName NetworkFeaturePodControllerLegacy::GetCatalogName() {
+  return QsFeatureCatalogName::kNetwork;
+}
+
 void NetworkFeaturePodControllerLegacy::OnIconPressed() {
   bool was_enabled = button_->IsToggled();
   bool can_toggle = SetNetworkEnabled(!was_enabled);
+  if (can_toggle)
+    TrackToggleUMA(/*target_toggle_state=*/!was_enabled);
 
   // If network was disabled, show network list as well as enabling network.
   // Also, if the network could not be toggled e.g. Ethernet, show network list.
-  if (!was_enabled || !can_toggle)
+  if (!was_enabled || !can_toggle) {
+    TrackDiveInUMA();
     tray_controller_->ShowNetworkDetailedView(!can_toggle /* force */);
+  }
 }
 
 void NetworkFeaturePodControllerLegacy::OnLabelPressed() {
+  TrackDiveInUMA();
   SetNetworkEnabled(true);
   tray_controller_->ShowNetworkDetailedView(true /* force */);
 }
diff --git a/ash/system/network/network_feature_pod_controller_legacy.h b/ash/system/network/network_feature_pod_controller_legacy.h
index 622a1d7..8c84908a 100644
--- a/ash/system/network/network_feature_pod_controller_legacy.h
+++ b/ash/system/network/network_feature_pod_controller_legacy.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_NETWORK_NETWORK_FEATURE_POD_CONTROLLER_LEGACY_H_
 #define ASH_SYSTEM_NETWORK_NETWORK_FEATURE_POD_CONTROLLER_LEGACY_H_
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
 namespace ash {
@@ -27,6 +28,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/network/network_feature_pod_controller_unittest.cc b/ash/system/network/network_feature_pod_controller_unittest.cc
index 2293b9f..f1f3b6f 100644
--- a/ash/system/network/network_feature_pod_controller_unittest.cc
+++ b/ash/system/network/network_feature_pod_controller_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -21,6 +22,7 @@
 #include "ash/system/unified/unified_system_tray_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/components/network/network_handler_callbacks.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
@@ -756,4 +758,46 @@
           feature_pod_icon_button()->GetImage(views::Button::STATE_NORMAL))));
 }
 
+TEST_F(NetworkFeaturePodControllerTest, UMATracking) {
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Disable WiFi.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kNetwork,
+      /*expected_count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Go to the detailed page.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kNetwork,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/network/vpn_feature_pod_controller.cc b/ash/system/network/vpn_feature_pod_controller.cc
index 08089e93..ee6b59c 100644
--- a/ash/system/network/vpn_feature_pod_controller.cc
+++ b/ash/system/network/vpn_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/network/vpn_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -65,7 +66,12 @@
   return button_;
 }
 
+QsFeatureCatalogName VPNFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kVPN;
+}
+
 void VPNFeaturePodController::OnIconPressed() {
+  TrackDiveInUMA();
   tray_controller_->ShowVPNDetailedView();
 }
 
diff --git a/ash/system/network/vpn_feature_pod_controller.h b/ash/system/network/vpn_feature_pod_controller.h
index c08641ad..96187ee6 100644
--- a/ash/system/network/vpn_feature_pod_controller.h
+++ b/ash/system/network/vpn_feature_pod_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_NETWORK_VPN_FEATURE_POD_CONTROLLER_H_
 #define ASH_SYSTEM_NETWORK_VPN_FEATURE_POD_CONTROLLER_H_
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/network/tray_network_state_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -26,6 +27,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/system/night_light/night_light_feature_pod_controller.cc b/ash/system/night_light/night_light_feature_pod_controller.cc
index da96d08..c8235f5 100644
--- a/ash/system/night_light/night_light_feature_pod_controller.cc
+++ b/ash/system/night_light/night_light_feature_pod_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/night_light/night_light_feature_pod_controller.h"
 #include <string>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
@@ -59,7 +60,15 @@
   return button_;
 }
 
+QsFeatureCatalogName NightLightFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kNightLight;
+}
+
 void NightLightFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(/*target_toggle_state=*/!Shell::Get()
+                     ->night_light_controller()
+                     ->GetEnabled());
+
   Shell::Get()->night_light_controller()->Toggle();
   LogUserNightLightEvent(Shell::Get()->night_light_controller()->GetEnabled());
   UpdateButton();
@@ -75,6 +84,7 @@
 
 void NightLightFeaturePodController::OnLabelPressed() {
   if (TrayPopupUtils::CanOpenWebUISettings()) {
+    TrackDiveInUMA();
     base::RecordAction(
         base::UserMetricsAction("StatusArea_NightLight_Settings"));
     tray_controller_->CloseBubble();  // Deletes |this|.
diff --git a/ash/system/night_light/night_light_feature_pod_controller.h b/ash/system/night_light/night_light_feature_pod_controller.h
index 7ba1df8c..b562611b6 100644
--- a/ash/system/night_light/night_light_feature_pod_controller.h
+++ b/ash/system/night_light/night_light_feature_pod_controller.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/model/clock_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -31,6 +32,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/night_light/night_light_feature_pod_controller_unittest.cc b/ash/system/night_light/night_light_feature_pod_controller_unittest.cc
index 9d3546d..2e99da9 100644
--- a/ash/system/night_light/night_light_feature_pod_controller_unittest.cc
+++ b/ash/system/night_light/night_light_feature_pod_controller_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/system/night_light/night_light_feature_pod_controller.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -13,6 +14,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/test/ash_test_base.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -50,6 +52,10 @@
     return feature_pod_button_->label_button_;
   }
 
+  void PressIcon() { feature_pod_controller_->OnIconPressed(); }
+
+  void PressLabel() { feature_pod_controller_->OnLabelPressed(); }
+
  private:
   std::unique_ptr<FeaturePodButton> feature_pod_button_;
   std::unique_ptr<NightLightFeaturePodController> feature_pod_controller_;
@@ -164,4 +170,79 @@
             feature_pod_label_button()->GetSubLabelText());
 }
 
+TEST_F(NightLightFeaturePodControllerTest, IconUMATracking) {
+  // Disable sunset-to-sunrise scheduling.
+  NightLightControllerImpl* controller = Shell::Get()->night_light_controller();
+  controller->SetScheduleType(NightLightController::ScheduleType::kNone);
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Toggle on the nightlight feature when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kNightLight,
+      /*expected_count=*/1);
+
+  // Toggle off the nightlight feature when pressing on the icon again.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kNightLight,
+      /*expected_count=*/1);
+}
+
+TEST_F(NightLightFeaturePodControllerTest, LabelUMATracking) {
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show nightlight detailed view (settings window) when pressing on the
+  // label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kNightLight,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/privacy_screen/privacy_screen_feature_pod_controller.cc b/ash/system/privacy_screen/privacy_screen_feature_pod_controller.cc
index 56bc7db..ba6928f 100644
--- a/ash/system/privacy_screen/privacy_screen_feature_pod_controller.cc
+++ b/ash/system/privacy_screen/privacy_screen_feature_pod_controller.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/display/privacy_screen_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -31,11 +32,14 @@
   return button_;
 }
 
-void PrivacyScreenFeaturePodController::OnIconPressed() {
-  TogglePrivacyScreen();
+QsFeatureCatalogName PrivacyScreenFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kPrivacyScreen;
 }
 
-void PrivacyScreenFeaturePodController::OnLabelPressed() {
+void PrivacyScreenFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(/*target_toggle_state=*/!Shell::Get()
+                     ->privacy_screen_controller()
+                     ->GetEnabled());
   TogglePrivacyScreen();
 }
 
diff --git a/ash/system/privacy_screen/privacy_screen_feature_pod_controller.h b/ash/system/privacy_screen/privacy_screen_feature_pod_controller.h
index 73d686e5..9cb2634 100644
--- a/ash/system/privacy_screen/privacy_screen_feature_pod_controller.h
+++ b/ash/system/privacy_screen/privacy_screen_feature_pod_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_PRIVACY_SCREEN_PRIVACY_SCREEN_FEATURE_POD_CONTROLLER_H_
 #define ASH_SYSTEM_PRIVACY_SCREEN_PRIVACY_SCREEN_FEATURE_POD_CONTROLLER_H_
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/display/privacy_screen_controller.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -25,8 +26,8 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
-  void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
  private:
diff --git a/ash/system/rotation/rotation_lock_feature_pod_controller.cc b/ash/system/rotation/rotation_lock_feature_pod_controller.cc
index a77d1c7ec..e1427c3 100644
--- a/ash/system/rotation/rotation_lock_feature_pod_controller.cc
+++ b/ash/system/rotation/rotation_lock_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/rotation/rotation_lock_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -34,7 +35,15 @@
   return button_;
 }
 
+QsFeatureCatalogName RotationLockFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kRotationLock;
+}
+
 void RotationLockFeaturePodController::OnIconPressed() {
+  TrackToggleUMA(/*target_toggle_state=*/!Shell::Get()
+                     ->screen_orientation_controller()
+                     ->user_rotation_locked());
+
   Shell::Get()->screen_orientation_controller()->ToggleUserRotationLock();
 }
 
diff --git a/ash/system/rotation/rotation_lock_feature_pod_controller.h b/ash/system/rotation/rotation_lock_feature_pod_controller.h
index cd93ad2..0f9ba5e 100644
--- a/ash/system/rotation/rotation_lock_feature_pod_controller.h
+++ b/ash/system/rotation/rotation_lock_feature_pod_controller.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_ROTATION_ROTATION_LOCK_FEATURE_POD_CONTROLLER_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
@@ -29,6 +30,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
 
diff --git a/ash/system/rotation/rotation_lock_feature_pod_controller_unittest.cc b/ash/system/rotation/rotation_lock_feature_pod_controller_unittest.cc
index 95cc6c29..6af0121b 100644
--- a/ash/system/rotation/rotation_lock_feature_pod_controller_unittest.cc
+++ b/ash/system/rotation/rotation_lock_feature_pod_controller_unittest.cc
@@ -4,11 +4,13 @@
 
 #include "ash/system/rotation/rotation_lock_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/shell.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "ui/display/display_switches.h"
 
 namespace ash {
@@ -113,4 +115,94 @@
   tablet_mode_controller->SetEnabledForTest(false);
 }
 
+TEST_F(RotationLockFeaturePodControllerTest, IconUMATracking) {
+  SetUpController();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Turn on rotation lock when pressing on the icon.
+  controller()->OnIconPressed();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kRotationLock,
+      /*expected_count=*/1);
+
+  // Turn off rotation lock when pressing on the icon.
+  controller()->OnIconPressed();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kRotationLock,
+      /*expected_count=*/1);
+}
+
+TEST_F(RotationLockFeaturePodControllerTest, LabelUMATracking) {
+  SetUpController();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Turn on rotation lock when pressing on the label.
+  controller()->OnLabelPressed();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kRotationLock,
+      /*expected_count=*/1);
+
+  // Turn off rotation lock when pressing on the label.
+  controller()->OnIconPressed();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kRotationLock,
+      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index 72414eed..43056bd 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -280,9 +280,7 @@
       delegate_->GetBackgroundColor().value_or(SK_ColorTRANSPARENT)));
 }
 
-TrayDetailedView::~TrayDetailedView() {
-  is_destroying_ = true;
-}
+TrayDetailedView::~TrayDetailedView() = default;
 
 void TrayDetailedView::OnViewClicked(views::View* sender) {
   HandleViewClicked(sender);
@@ -490,9 +488,6 @@
 void TrayDetailedView::OnThemeChanged() {
   views::View::OnThemeChanged();
 
-  if (is_destroying_)
-    return;
-
   delegate_->UpdateColors();
 
   auto* color_provider = AshColorProvider::Get();
diff --git a/ash/system/tray/tray_detailed_view.h b/ash/system/tray/tray_detailed_view.h
index e6d149e..7b8dfba 100644
--- a/ash/system/tray/tray_detailed_view.h
+++ b/ash/system/tray/tray_detailed_view.h
@@ -168,11 +168,6 @@
   // separator.
   bool has_separator_ = true;
 
-  // True once the destructor is called. Used to prevent `OnThemeChanged` from
-  // being called at the same time when this view is being removed. See
-  // https://crbug.com/1353195 for more details.
-  bool is_destroying_ = false;
-
   // The accessible name for the `progress_bar_`.
   absl::optional<std::u16string> progress_bar_accessible_name_;
 };
diff --git a/ash/system/unified/feature_pod_controller_base.cc b/ash/system/unified/feature_pod_controller_base.cc
index 5601cb9c..3c5ba25 100644
--- a/ash/system/unified/feature_pod_controller_base.cc
+++ b/ash/system/unified/feature_pod_controller_base.cc
@@ -4,10 +4,21 @@
 
 #include "ash/system/unified/feature_pod_controller_base.h"
 
+#include "ash/system/unified/quick_settings_metrics_util.h"
+
 namespace ash {
 
 void FeaturePodControllerBase::OnLabelPressed() {
   return OnIconPressed();
 }
 
+void FeaturePodControllerBase::TrackToggleUMA(bool target_toggle_state) {
+  quick_settings_metrics_util::RecordQsFeatureToggle(GetCatalogName(),
+                                                     target_toggle_state);
+}
+
+void FeaturePodControllerBase::TrackDiveInUMA() {
+  quick_settings_metrics_util::RecordQsFeatureDiveIn(GetCatalogName());
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/feature_pod_controller_base.h b/ash/system/unified/feature_pod_controller_base.h
index 4c2e6f3..fccb541 100644
--- a/ash/system/unified/feature_pod_controller_base.h
+++ b/ash/system/unified/feature_pod_controller_base.h
@@ -6,6 +6,7 @@
 #define ASH_SYSTEM_UNIFIED_FEATURE_POD_CONTROLLER_BASE_H_
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/tray/system_tray_item_uma_type.h"
 
 namespace ash {
@@ -25,6 +26,12 @@
   // this).
   virtual FeaturePodButton* CreateButton() = 0;
 
+  // Returns the feature catalog name which is used for UMA tracking. Please
+  // remember to call the corresponding tracking method (`TrackToggleUMA` and
+  // `TrackDiveInUMA`) in the `OnIconPressed` and OnLabelPressed`
+  // implementation.
+  virtual QsFeatureCatalogName GetCatalogName() = 0;
+
   // Called when the icon of the feature pod button is clicked.
   // If the feature pod is togglable, it is expected to toggle the feature.
   virtual void OnIconPressed() = 0;
@@ -37,6 +44,17 @@
   // Return histogram value for Ash.SystemMenu.DefaultView.VisibleRows. If the
   // button is not recorded, UMA_NOT_RECORDED will be used.
   virtual SystemTrayItemUmaType GetUmaType() const = 0;
+
+  // Tracks the toggling behavior, usually happens `OnIconPressed`. But this
+  // method can also be called in the `OnLabelPressed` method, when pressing on
+  // the label has the same behavior as pressing on the icon. If the feature has
+  // no `target_toggle_state` state, such as the screen capture feaure, pass
+  // `true` to this method.
+  void TrackToggleUMA(bool target_toggle_state);
+
+  // Tracks the navigating to detailed page behavior, usually happens
+  // `OnLabelPressed`, sometimes also happens `OnIconPressed`.
+  void TrackDiveInUMA();
 };
 
 }  // namespace ash
diff --git a/ash/system/unified/feature_pods_container_view_unittest.cc b/ash/system/unified/feature_pods_container_view_unittest.cc
index f4c7729..d44613b 100644
--- a/ash/system/unified/feature_pods_container_view_unittest.cc
+++ b/ash/system/unified/feature_pods_container_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/unified/feature_pods_container_view.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/system/tray/tray_constants.h"
@@ -52,11 +53,17 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override { return nullptr; }
+
   void OnIconPressed() override {}
+
   SystemTrayItemUmaType GetUmaType() const override {
     return SystemTrayItemUmaType::UMA_TEST;
   }
 
+  QsFeatureCatalogName GetCatalogName() override {
+    return QsFeatureCatalogName::kUnknown;
+  }
+
   // views::ViewObserver:
   void OnViewPreferredSizeChanged(views::View* observed_view) override {
     ++preferred_size_changed_count_;
diff --git a/ash/system/unified/quick_settings_metrics_util.cc b/ash/system/unified/quick_settings_metrics_util.cc
index 4638e71..fe4d731 100644
--- a/ash/system/unified/quick_settings_metrics_util.cc
+++ b/ash/system/unified/quick_settings_metrics_util.cc
@@ -5,7 +5,9 @@
 #include "ash/system/unified/quick_settings_metrics_util.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "ui/events/event.h"
 
 namespace ash {
@@ -14,21 +16,79 @@
 
 // For the revamped view:
 constexpr char kQuickSettingsButton[] = "Ash.QuickSettings.Button.Activated";
+constexpr char kQuickSettingsFeaturePodEnabled[] =
+    "Ash.QuickSettings.FeaturePod.ToggledOn";
+constexpr char kQuickSettingsFeaturePodDisabled[] =
+    "Ash.QuickSettings.FeaturePod.ToggledOff";
+constexpr char kQuickSettingsFeaturePodDiveIn[] =
+    "Ash.QuickSettings.FeaturePod.DiveIn";
+constexpr char kQuickSettingsFeaturePodCount[] =
+    "Ash.QuickSettings.Clamshell.FeaturePodCountOnOpen";
+constexpr char kQuickSettingsTabletFeaturePodCount[] =
+    "Ash.QuickSettings.Tablet.FeaturePodCountOnOpen";
 
 // For the old view:
 constexpr char kUnifiedViewButton[] = "Ash.UnifiedSystemView.Button.Activated";
+constexpr char kUnifiedViewFeaturePodEnabled[] =
+    "Ash.UnifiedSystemView.FeaturePod.ToggledOn";
+constexpr char kUnifiedViewFeaturePodDisabled[] =
+    "Ash.UnifiedSystemView.FeaturePod.ToggledOff";
+constexpr char kUnifiedViewFeaturePodDiveIn[] =
+    "Ash.UnifiedSystemView.FeaturePod.DiveIn";
+constexpr char kUnifiedViewFeaturePodCount[] =
+    "Ash.UnifiedSystemView.Clamshell.FeaturePodCountOnOpen";
+constexpr char kUnifiedViewTabletFeaturePodCount[] =
+    "Ash.UnifiedSystemView.Tablet.FeaturePodCountOnOpen";
 
 }  // namespace
 
 namespace quick_settings_metrics_util {
 
-void RecordQsButtonActivated(const QsButtonCatalogName button_catalog_name,
+void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name,
                              const ui::Event& event) {
   base::UmaHistogramEnumeration(
       features::IsQsRevampEnabled() ? kQuickSettingsButton : kUnifiedViewButton,
       button_catalog_name);
 }
 
+void RecordQsFeatureToggle(QsFeatureCatalogName feature_catalog_name,
+                           bool enable) {
+  if (enable) {
+    base::UmaHistogramEnumeration(features::IsQsRevampEnabled()
+                                      ? kQuickSettingsFeaturePodEnabled
+                                      : kUnifiedViewFeaturePodEnabled,
+                                  feature_catalog_name);
+    return;
+  }
+
+  base::UmaHistogramEnumeration(features::IsQsRevampEnabled()
+                                    ? kQuickSettingsFeaturePodDisabled
+                                    : kUnifiedViewFeaturePodDisabled,
+                                feature_catalog_name);
+}
+
+void RecordQsFeatureDiveIn(QsFeatureCatalogName feature_catalog_name) {
+  base::UmaHistogramEnumeration(features::IsQsRevampEnabled()
+                                    ? kQuickSettingsFeaturePodDiveIn
+                                    : kUnifiedViewFeaturePodDiveIn,
+                                feature_catalog_name);
+}
+
+void RecordQsFeaturePodCount(int feature_pod_count, bool is_tablet) {
+  if (is_tablet) {
+    UMA_HISTOGRAM_COUNTS_100(features::IsQsRevampEnabled()
+                                 ? kQuickSettingsTabletFeaturePodCount
+                                 : kUnifiedViewTabletFeaturePodCount,
+                             feature_pod_count);
+    return;
+  }
+
+  UMA_HISTOGRAM_COUNTS_100(features::IsQsRevampEnabled()
+                               ? kQuickSettingsFeaturePodCount
+                               : kUnifiedViewFeaturePodCount,
+                           feature_pod_count);
+}
+
 }  // namespace quick_settings_metrics_util
 
 }  // namespace ash
diff --git a/ash/system/unified/quick_settings_metrics_util.h b/ash/system/unified/quick_settings_metrics_util.h
index 6b75eb3..aa9be80 100644
--- a/ash/system/unified/quick_settings_metrics_util.h
+++ b/ash/system/unified/quick_settings_metrics_util.h
@@ -19,9 +19,22 @@
 // the enum bucket for now. Leaves the `event` as a arg in the method for later
 // use, so that if the event type need to be tracked later we can simply add
 // them in this method.
-void RecordQsButtonActivated(const QsButtonCatalogName button_catalog_name,
+void RecordQsButtonActivated(QsButtonCatalogName button_catalog_name,
                              const ui::Event& event);
 
+// Records toggle to enable/disable a feature in the quick settings main page.
+// The arg `enable == true` means this feature was disabled and will be enabled
+// by this toggle action. If the feature pod is an action feature, such as
+// screen caption, always use `true` as the toggled value.
+void RecordQsFeatureToggle(QsFeatureCatalogName feature_catalog_name,
+                           bool enable);
+
+// Records dive into a feature's details page from the quick settings main page.
+void RecordQsFeatureDiveIn(QsFeatureCatalogName feature_catalog_name);
+
+// Records visible feature pod number in the quick settings main page.
+void RecordQsFeaturePodCount(int feature_pod_count, bool is_tablet);
+
 }  // namespace ash::quick_settings_metrics_util
 
 #endif  // ASH_SYSTEM_UNIFIED_QUICK_SETTINGS_METRICS_UTIL_H_
diff --git a/ash/system/unified/quiet_mode_feature_pod_controller.cc b/ash/system/unified/quiet_mode_feature_pod_controller.cc
index 00a88e5..f0245cd 100644
--- a/ash/system/unified/quiet_mode_feature_pod_controller.cc
+++ b/ash/system/unified/quiet_mode_feature_pod_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/unified/quiet_mode_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/notifier_metadata.h"
 #include "ash/public/cpp/notifier_settings_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -61,9 +62,14 @@
   return button_;
 }
 
+QsFeatureCatalogName QuietModeFeaturePodController::GetCatalogName() {
+  return QsFeatureCatalogName::kQuietMode;
+}
+
 void QuietModeFeaturePodController::OnIconPressed() {
   MessageCenter* message_center = MessageCenter::Get();
   bool is_quiet_mode = message_center->IsQuietMode();
+  TrackToggleUMA(/*target_toggle_state=*/!is_quiet_mode);
   LogUserQuietModeEvent(!is_quiet_mode);
   message_center->SetQuietMode(!is_quiet_mode);
 
@@ -76,6 +82,7 @@
 }
 
 void QuietModeFeaturePodController::OnLabelPressed() {
+  TrackDiveInUMA();
   tray_controller_->ShowNotifierSettingsView();
 }
 
diff --git a/ash/system/unified/quiet_mode_feature_pod_controller.h b/ash/system/unified/quiet_mode_feature_pod_controller.h
index 0e3fdba2..a922efb 100644
--- a/ash/system/unified/quiet_mode_feature_pod_controller.h
+++ b/ash/system/unified/quiet_mode_feature_pod_controller.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/ash_export.h"
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/notifier_settings_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -36,6 +37,7 @@
 
   // FeaturePodControllerBase:
   FeaturePodButton* CreateButton() override;
+  QsFeatureCatalogName GetCatalogName() override;
   void OnIconPressed() override;
   void OnLabelPressed() override;
   SystemTrayItemUmaType GetUmaType() const override;
diff --git a/ash/system/unified/quiet_mode_feature_pod_controller_unittest.cc b/ash/system/unified/quiet_mode_feature_pod_controller_unittest.cc
index aa9b365f..676f1cee8 100644
--- a/ash/system/unified/quiet_mode_feature_pod_controller_unittest.cc
+++ b/ash/system/unified/quiet_mode_feature_pod_controller_unittest.cc
@@ -4,11 +4,13 @@
 
 #include "ash/system/unified/quiet_mode_feature_pod_controller.h"
 
+#include "ash/constants/quick_settings_catalogs.h"
 #include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
-#include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
-#include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
 
 namespace ash {
 
@@ -27,16 +29,12 @@
   void SetUp() override {
     NoSessionAshTestBase::SetUp();
 
-    tray_model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    tray_controller_ =
-        std::make_unique<UnifiedSystemTrayController>(tray_model_.get());
+    GetPrimaryUnifiedSystemTray()->ShowBubble();
   }
 
   void TearDown() override {
     button_.reset();
     controller_.reset();
-    tray_controller_.reset();
-    tray_model_.reset();
     NoSessionAshTestBase::TearDown();
   }
 
@@ -48,14 +46,18 @@
   }
 
   UnifiedSystemTrayController* tray_controller() {
-    return tray_controller_.get();
+    return GetPrimaryUnifiedSystemTray()
+        ->bubble()
+        ->unified_system_tray_controller();
   }
 
+  void PressIcon() { controller_->OnIconPressed(); }
+
+  void PressLabel() { controller_->OnLabelPressed(); }
+
   FeaturePodButton* button() { return button_.get(); }
 
  private:
-  scoped_refptr<UnifiedSystemTrayModel> tray_model_;
-  std::unique_ptr<UnifiedSystemTrayController> tray_controller_;
   std::unique_ptr<QuietModeFeaturePodController> controller_;
   std::unique_ptr<FeaturePodButton> button_;
 };
@@ -81,4 +83,81 @@
   EXPECT_FALSE(button()->GetVisible());
 }
 
+TEST_F(QuietModeFeaturePodControllerTest, IconUMATracking) {
+  CreateUserSessions(1);
+  SetUpButton();
+  message_center::MessageCenter::Get()->SetQuietMode(false);
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Turn on quiet mode when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      QsFeatureCatalogName::kQuietMode,
+      /*expected_count=*/1);
+
+  // Turn off quiet mode when pressing on the icon.
+  PressIcon();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/1);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+  histogram_tester->ExpectBucketCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      QsFeatureCatalogName::kQuietMode,
+      /*expected_count=*/1);
+}
+
+TEST_F(QuietModeFeaturePodControllerTest, LabelUMATracking) {
+  CreateUserSessions(1);
+  SetUpButton();
+
+  // No metrics logged before clicking on any views.
+  auto histogram_tester = std::make_unique<base::HistogramTester>();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/0);
+
+  // Show quiet mode detailed view when pressing on the label.
+  PressLabel();
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOn",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount(
+      "Ash.UnifiedSystemView.FeaturePod.ToggledOff",
+      /*count=*/0);
+  histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                     /*count=*/1);
+  histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
+                                      QsFeatureCatalogName::kQuietMode,
+                                      /*expected_count=*/1);
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index 2755e618..8e9a206 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -57,6 +57,7 @@
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 #include "ash/system/unified/feature_pods_container_view.h"
+#include "ash/system/unified/quick_settings_metrics_util.h"
 #include "ash/system/unified/quick_settings_view.h"
 #include "ash/system/unified/quiet_mode_feature_pod_controller.h"
 #include "ash/system/unified/unified_notifier_settings_controller.h"
@@ -512,7 +513,12 @@
   }
 
   showing_audio_detailed_view_ = false;
-  detailed_view_controller_.reset();
+
+  // Transfer `detailed_view_controller_` to a scoped object, which will be
+  // destroyed once it's out of this method's scope (after resetting
+  // `quick_settings_view_`'s `detailed_view_`). Because the detailed view has a
+  // reference to its `detailed_view_controller_` which is used in shutdown.
+  auto scoped_detailed_view_controller = std::move(detailed_view_controller_);
 
   if (features::IsQsRevampEnabled()) {
     quick_settings_view_->ResetDetailedView();
@@ -548,11 +554,15 @@
 void UnifiedSystemTrayController::EnsureExpanded() {
   if (detailed_view_controller_) {
     showing_audio_detailed_view_ = false;
-    detailed_view_controller_.reset();
     if (features::IsQsRevampEnabled())
       quick_settings_view_->ResetDetailedView();
     else
       unified_view_->ResetDetailedView();
+
+    // Destroy `detailed_view_controller_` after resetting
+    // `quick_settings_view_`'s `detailed_view_` because the detailed view has a
+    // reference to its `detailed_view_controller_` which is used in shutdown.
+    detailed_view_controller_.reset();
   }
   StartAnimation(true /*expand*/);
 
@@ -641,25 +651,14 @@
 
   // If you want to add a new feature pod item, add here.
   if (features::IsQsRevampEnabled()) {
-    if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
-      UMA_HISTOGRAM_COUNTS_100(
-          "ChromeOS.SystemTray.Tablet.FeaturePodCountOnOpen",
-          quick_settings_view_->GetVisibleFeaturePodCount());
-    } else {
-      UMA_HISTOGRAM_COUNTS_100(
-          "ChromeOS.SystemTray.FeaturePodCountOnOpen",
-          quick_settings_view_->GetVisibleFeaturePodCount());
-    }
+    quick_settings_metrics_util::RecordQsFeaturePodCount(
+        quick_settings_view_->GetVisibleFeaturePodCount(),
+        Shell::Get()->tablet_mode_controller()->InTabletMode());
     return;
   }
-
-  if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
-    UMA_HISTOGRAM_COUNTS_100("ChromeOS.SystemTray.Tablet.FeaturePodCountOnOpen",
-                             unified_view_->GetVisibleFeaturePodCount());
-  } else {
-    UMA_HISTOGRAM_COUNTS_100("ChromeOS.SystemTray.FeaturePodCountOnOpen",
-                             unified_view_->GetVisibleFeaturePodCount());
-  }
+  quick_settings_metrics_util::RecordQsFeaturePodCount(
+      unified_view_->GetVisibleFeaturePodCount(),
+      Shell::Get()->tablet_mode_controller()->InTabletMode());
 }
 
 void UnifiedSystemTrayController::AddFeaturePodItem(
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index 052fe8369..9d681675 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -365,8 +365,9 @@
     GetCurrentOsVersionCallback callback) {
   DCHECK(features::IsShimlessRMAOsUpdateEnabled());
   // TODO(gavindodd): Decide whether to use full or short Chrome version.
-  std::move(callback).Run(chromeos::version_loader::GetVersion(
-      chromeos::version_loader::VERSION_FULL));
+  absl::optional<std::string> version = chromeos::version_loader::GetVersion(
+      chromeos::version_loader::VERSION_FULL);
+  std::move(callback).Run(version);
 }
 
 void ShimlessRmaService::CheckForOsUpdates(CheckForOsUpdatesCallback callback) {
diff --git a/ash/webui/shimless_rma/mojom/shimless_rma.mojom b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
index 345a1ae..27d9165 100644
--- a/ash/webui/shimless_rma/mojom/shimless_rma.mojom
+++ b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
@@ -539,7 +539,7 @@
   // Methods for kUpdateOs state.
   //
   // Returns a string representation of the OS version.
-  GetCurrentOsVersion() => (string version);
+  GetCurrentOsVersion() => (string? version);
   // Returns true if there is an OS update available, false otherwise.
   CheckForOsUpdates() => (bool update_available, string version);
   // Attempts to start a Chrome OS update.
diff --git a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
index 9c873f1..6ce534c 100644
--- a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
@@ -215,7 +215,7 @@
   }
 
   /**
-   * @param {string} version
+   * @param {null|string} version
    */
   setGetCurrentOsVersionResult(version) {
     this.methods_.setResult('getCurrentOsVersion', {version: version});
diff --git a/ash/webui/shimless_rma/resources/onboarding_update_page.js b/ash/webui/shimless_rma/resources/onboarding_update_page.js
index 2a8b061..f7c7be1 100644
--- a/ash/webui/shimless_rma/resources/onboarding_update_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_update_page.js
@@ -165,7 +165,11 @@
       return;
     }
     this.shimlessRmaService_.getCurrentOsVersion().then((res) => {
-      this.currentVersion_ = res.version;
+      if (res.version != null) {
+        this.currentVersion_ = res.version;
+      } else {
+        this.currentVersion_ = '0.0.0.0';
+      }
       this.currentVersionText_ =
           this.i18n('currentVersionOutOfDateText', this.currentVersion_);
     });
diff --git a/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js b/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
index ff466c6b..a69d709 100644
--- a/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
+++ b/ash/webui/shimless_rma/resources/reimaging_firmware_update_page.js
@@ -12,8 +12,8 @@
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getShimlessRmaService} from './mojo_interface_provider.js';
-import {ShimlessRmaServiceInterface, StateResult, UpdateRoFirmwareObserverInterface, UpdateRoFirmwareObserverReceiver, UpdateRoFirmwareStatus} from './shimless_rma_types.js';
-import {disableNextButton, enableNextButton, focusPageTitle} from './shimless_rma_util.js';
+import {ExternalDiskStateObserverInterface, ExternalDiskStateObserverReceiver, ShimlessRmaServiceInterface, StateResult, UpdateRoFirmwareObserverInterface, UpdateRoFirmwareObserverReceiver, UpdateRoFirmwareStatus} from './shimless_rma_types.js';
+import {executeThenTransitionState, focusPageTitle} from './shimless_rma_util.js';
 
 /** @type {!Object<!UpdateRoFirmwareStatus, string>} */
 const STATUS_TEXT_KEY_MAP = {
@@ -123,6 +123,13 @@
 
     this.shimlessRmaService_.observeRoFirmwareUpdateProgress(
         this.updateRoFirmwareObserverReceiver_.$.bindNewPipeAndPassRemote());
+
+    /** @private {!ExternalDiskStateObserverReceiver} */
+    this.externalDiskStateReceiver_ = new ExternalDiskStateObserverReceiver(
+        /** @type {!ExternalDiskStateObserverInterface} */ (this));
+
+    this.shimlessRmaService_.observeExternalDiskState(
+        this.externalDiskStateReceiver_.$.bindNewPipeAndPassRemote());
   }
 
   /** @override */
@@ -146,21 +153,16 @@
     this.shouldShowSpinner_ = this.status_ === UpdateRoFirmwareStatus.kUpdating;
     this.shouldShowWarning_ =
         this.status_ === UpdateRoFirmwareStatus.kFileNotFound;
-
-    const disabled = this.status_ != UpdateRoFirmwareStatus.kComplete;
-    if (disabled) {
-      disableNextButton(this);
-    } else {
-      enableNextButton(this);
-    }
   }
 
-  /** @return {!Promise<!{stateResult: !StateResult}>} */
-  onNextButtonClick() {
-    if (this.status_ == UpdateRoFirmwareStatus.kComplete) {
-      return this.shimlessRmaService_.roFirmwareUpdateComplete();
-    } else {
-      return Promise.reject(new Error('RO Firmware update is not complete.'));
+  /**
+   * Implements ExternalDiskStateObserver.onExternalDiskStateChanged()
+   * @param {boolean} detected
+   */
+  onExternalDiskStateChanged(detected) {
+    if (!detected && this.status_ === UpdateRoFirmwareStatus.kComplete) {
+      executeThenTransitionState(
+          this, () => this.shimlessRmaService_.roFirmwareUpdateComplete());
     }
   }
 
diff --git a/ash/webui/shimless_rma/resources/shimless_rma.js b/ash/webui/shimless_rma/resources/shimless_rma.js
index 47c5af33..c0fa0684 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma.js
@@ -153,7 +153,7 @@
   [State.kUpdateRoFirmware]: {
     componentIs: 'reimaging-firmware-update-page',
     requiresReloadWhenShown: false,
-    buttonNext: ButtonState.DISABLED,
+    buttonNext: ButtonState.HIDDEN,
     buttonExit: ButtonState.HIDDEN,
     buttonBack: ButtonState.HIDDEN,
   },
diff --git a/ash/wm/desks/templates/restore_data_collector.cc b/ash/wm/desks/templates/restore_data_collector.cc
index fd62046..390d90c2 100644
--- a/ash/wm/desks/templates/restore_data_collector.cc
+++ b/ash/wm/desks/templates/restore_data_collector.cc
@@ -59,6 +59,8 @@
 
     if (!delegate->IsWindowSupportedForDeskTemplate(window)) {
       call.unsupported_apps.push_back(window);
+      if (delegate->IsIncognitoWindow(window))
+        call.incognito_window_count++;
       continue;
     }
 
@@ -161,7 +163,7 @@
     // There were some unsupported apps in the active desk so open up a dialog
     // to let the user know.
     saved_desk_util::GetSavedDeskDialogController()->ShowUnsupportedAppsDialog(
-        root_window_to_show, std::move(call.unsupported_apps),
+        root_window_to_show, call.unsupported_apps, call.incognito_window_count,
         std::move(call.callback), std::move(desk_template));
   } else {
     std::move(call.callback).Run(std::move(desk_template));
diff --git a/ash/wm/desks/templates/restore_data_collector.h b/ash/wm/desks/templates/restore_data_collector.h
index 57617f8..24ce2e3 100644
--- a/ash/wm/desks/templates/restore_data_collector.h
+++ b/ash/wm/desks/templates/restore_data_collector.h
@@ -60,6 +60,7 @@
     std::string template_name;
     aura::Window* root_window_to_show;
     std::vector<aura::Window*> unsupported_apps;
+    size_t incognito_window_count = 0;
     std::unique_ptr<app_restore::RestoreData> data;
     uint32_t pending_request_count = 0;
     GetDeskTemplateCallback callback;
diff --git a/ash/wm/desks/templates/saved_desk_dialog_controller.cc b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
index 1a50ed20..b7306497 100644
--- a/ash/wm/desks/templates/saved_desk_dialog_controller.cc
+++ b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
@@ -130,6 +130,7 @@
 void SavedDeskDialogController::ShowUnsupportedAppsDialog(
     aura::Window* root_window,
     const std::vector<aura::Window*>& unsupported_apps,
+    size_t incognito_window_count,
     DesksController::GetDeskTemplateCallback callback,
     std::unique_ptr<DeskTemplate> desk_template) {
   // We can only bind `callback` once but the user has three possible paths
@@ -138,30 +139,11 @@
   unsupported_apps_callback_ = std::move(callback);
   unsupported_apps_template_ = std::move(desk_template);
 
-  size_t incognito_window_count = 0;
-  bool contains_lacros_window = false;
-  auto* delegate = Shell::Get()->desks_templates_delegate();
-  // TODO(shidi): The caller of  ShowUnsupportedAppsDialog should provide us
-  // with the incognito window count to avoid double looping.
-  for (auto* window : unsupported_apps) {
-    if (static_cast<AppType>(window->GetProperty(aura::client::kAppType)) ==
-        AppType::LACROS) {
-      contains_lacros_window = true;
-      break;
-    }
-
-    if (delegate->IsIncognitoWindow(window))
-      ++incognito_window_count;
-  }
-
   // Note that this assumed unsupported apps which are not incognito browsers
   // are linux apps.
   std::u16string app_description;
   int app_description_id;
-  if (contains_lacros_window) {
-    app_description_id =
-        IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LACROS_DIALOG_DESCRIPTION;
-  } else if (incognito_window_count == 0) {
+  if (incognito_window_count == 0) {
     app_description_id =
         IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_LINUX_APPS_DIALOG_DESCRIPTION;
   } else if (incognito_window_count != unsupported_apps.size()) {
diff --git a/ash/wm/desks/templates/saved_desk_dialog_controller.h b/ash/wm/desks/templates/saved_desk_dialog_controller.h
index 5e1a6661..e1c75394 100644
--- a/ash/wm/desks/templates/saved_desk_dialog_controller.h
+++ b/ash/wm/desks/templates/saved_desk_dialog_controller.h
@@ -43,6 +43,7 @@
   void ShowUnsupportedAppsDialog(
       aura::Window* root_window,
       const std::vector<aura::Window*>& unsupported_apps,
+      size_t incognito_window_count,
       DesksController::GetDeskTemplateCallback callback,
       std::unique_ptr<DeskTemplate> desk_template);
   void ShowReplaceDialog(aura::Window* root_window,
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index 84c3d44..4c436f8 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -521,6 +521,18 @@
   EXPECT_FALSE(immersive_controller->IsEnabled());
 }
 
+// Tests that if we minimize a floated window, it is floated upon unminimizing.
+TEST_F(TabletWindowFloatTest, UnminimizeFloatWindow) {
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+
+  std::unique_ptr<aura::Window> window = CreateFloatedWindow();
+  auto* window_state = WindowState::Get(window.get());
+
+  window_state->Minimize();
+  window_state->Unminimize();
+  EXPECT_TRUE(window_state->IsFloated());
+}
+
 TEST_F(TabletWindowFloatTest, Rotation) {
   // Use a display where the width and height are quite different, otherwise it
   // would be hard to tell if portrait mode is using landscape bounds to
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index 906b73a2..f2855b8a 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -914,7 +914,7 @@
   EXPECT_TRUE(window_state->IsMaximized());
 }
 
-// Tests unminimizing in tablet mode and then existing tablet mode should have
+// Tests unminimizing in tablet mode and then exiting tablet mode should have
 // pre-minimized window show state.
 TEST_F(TabletModeWindowManagerTest, UnminimizeInTabletMode) {
   // Tests restoring to maximized show state.
@@ -939,6 +939,20 @@
   EXPECT_EQ(gfx::Rect(10, 10, 100, 100), window->GetBoundsInScreen());
 }
 
+// Tests that if we minimize a snapped window, it is snapped upon unminimizing.
+TEST_F(TabletModeWindowManagerTest, UnminimizeSnapInTabletMode) {
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  std::unique_ptr<aura::Window> window = CreateAppWindow();
+  auto* window_state = WindowState::Get(window.get());
+  WindowSnapWMEvent event(WM_EVENT_SNAP_PRIMARY);
+  window_state->OnWMEvent(&event);
+  ASSERT_TRUE(window_state->IsSnapped());
+
+  window_state->Minimize();
+  window_state->Unminimize();
+  EXPECT_TRUE(window_state->IsSnapped());
+}
+
 // Check that a full screen window remains full screen upon entering maximize
 // mode. Furthermore, checks that this window is not full screen upon exiting
 // tablet mode if it was un-full-screened while in tablet mode.
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.cc b/ash/wm/tablet_mode/tablet_mode_window_state.cc
index 6e8d6ec..8cab805 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.cc
@@ -28,6 +28,7 @@
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/wm/features.h"
 #include "chromeos/ui/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/compositor/layer.h"
@@ -314,26 +315,25 @@
     case WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE:
     case WM_EVENT_TOGGLE_MAXIMIZE:
     case WM_EVENT_CENTER:
-    case WM_EVENT_NORMAL:
     case WM_EVENT_MAXIMIZE:
       UpdateWindow(window_state,
                    window_state->GetMaximizedOrCenteredWindowType(),
                    true /* animated */);
       return;
-    case WM_EVENT_RESTORE: {
-      // We special handle WM_EVENT_RESTORE event here.
-      WindowStateType restore_state = window_state->GetRestoreWindowState();
-      if (restore_state == WindowStateType::kPrimarySnapped) {
-        window_state->set_snap_action_source(
-            WindowSnapActionSource::kSnapByWindowStateRestore);
-        DoTabletSnap(window_state, WM_EVENT_SNAP_PRIMARY);
-      } else if (restore_state == WindowStateType::kSecondarySnapped) {
-        window_state->set_snap_action_source(
-            WindowSnapActionSource::kSnapByWindowStateRestore);
-        DoTabletSnap(window_state, WM_EVENT_SNAP_SECONDARY);
+    case WM_EVENT_NORMAL: {
+      // `WM_EVENT_NORMAL` may be restoring state from minimized.
+      if (window_state->window()->GetProperty(aura::client::kIsRestoringKey)) {
+        DoRestore(window_state);
       } else {
-        UpdateWindow(window_state, restore_state, /*animate=*/true);
+        UpdateWindow(window_state,
+                     window_state->GetMaximizedOrCenteredWindowType(),
+                     /*animate=*/true);
       }
+      return;
+    }
+    case WM_EVENT_RESTORE: {
+      // We special handle `WM_EVENT_RESTORE` event here.
+      DoRestore(window_state);
       break;
     }
     case WM_EVENT_FLOAT:
@@ -342,7 +342,7 @@
         return;
 
       UpdateWindow(window_state, WindowStateType::kFloated,
-                   /*=animated=*/true);
+                   /*=animate=*/true);
       break;
     case WM_EVENT_SNAP_PRIMARY:
     case WM_EVENT_SNAP_SECONDARY:
@@ -623,4 +623,18 @@
   UpdateWindow(window_state, new_state_type, /*animated=*/false);
 }
 
+void TabletModeWindowState::DoRestore(WindowState* window_state) {
+  WindowStateType restore_state = window_state->GetRestoreWindowState();
+  if (chromeos::IsSnappedWindowStateType(restore_state)) {
+    window_state->set_snap_action_source(
+        WindowSnapActionSource::kSnapByWindowStateRestore);
+    DoTabletSnap(window_state, restore_state == WindowStateType::kPrimarySnapped
+                                   ? WM_EVENT_SNAP_PRIMARY
+                                   : WM_EVENT_SNAP_SECONDARY);
+    return;
+  }
+
+  UpdateWindow(window_state, restore_state, /*animate=*/true);
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_state.h b/ash/wm/tablet_mode/tablet_mode_window_state.h
index 57990642..2343b14 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_state.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_state.h
@@ -92,6 +92,10 @@
   // Snap the window in tablet split view if it can be snapped.
   void DoTabletSnap(WindowState* window_state, WMEventType snap_event_type);
 
+  // Called by `WM_EVENT_RESTORE`, or a `WM_EVENT_NORMAL` that is restoring.
+  // Restores to the state in `window_states`'s restore history.
+  void DoRestore(WindowState* window_state);
+
   // The original bounds and state object of the window.
   gfx::Rect old_window_bounds_in_screen_;
   std::unique_ptr<WindowState::State> old_state_;
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h
index 13cb0a28..3bbc5c1 100644
--- a/base/memory/raw_ptr.h
+++ b/base/memory/raw_ptr.h
@@ -287,9 +287,25 @@
     //
     // Disambiguation: UntagPtr removes the hardware MTE tag, whereas this
     // class is responsible for handling the software MTE tag.
-    CHECK(ExtractTag(partition_alloc::UntagPtr(wrapped_ptr1)) ==
-          ExtractTag(partition_alloc::UntagPtr(wrapped_ptr2)));
-    return wrapped_ptr1 - wrapped_ptr2;
+    //
+    // MTECheckedPtr doesn't use 0 as a valid tag; depending on which
+    // subtraction operator is called, we may be getting the actual
+    // untagged T* or the wrapped pointer (passed as a T*) in one or
+    // both args. We can only check slot cohabitation when both args
+    // come with tags.
+    const uintptr_t tag1 = ExtractTag(partition_alloc::UntagPtr(wrapped_ptr1));
+    const uintptr_t tag2 = ExtractTag(partition_alloc::UntagPtr(wrapped_ptr2));
+    if (tag1 && tag2) {
+      CHECK(tag1 == tag2);
+      return wrapped_ptr1 - wrapped_ptr2;
+    }
+
+    // If one or the other arg come untagged, we have to perform the
+    // subtraction entirely without tags.
+    return reinterpret_cast<T*>(
+               ExtractAddress(partition_alloc::UntagPtr(wrapped_ptr1))) -
+           reinterpret_cast<T*>(
+               ExtractAddress(partition_alloc::UntagPtr(wrapped_ptr2)));
   }
 
   // Returns a copy of a wrapped pointer, without making an assertion
diff --git a/base/scoped_observation.h b/base/scoped_observation.h
index cd2c0715..9aad8b4 100644
--- a/base/scoped_observation.h
+++ b/base/scoped_observation.h
@@ -13,35 +13,50 @@
 
 namespace base {
 
-// ScopedObservation is used to keep track of singular observation, e.g.
-// where an observer observes a single source only.
-//
-// Use base::ScopedMultiSourceObservation for objects that observe multiple
+// `ScopedObservation` is used to keep track of a singular observation, i.e.,
+// where an observer observes a single source only. Use
+// `base::ScopedMultiSourceObservation` for objects that observe multiple
 // sources.
 //
-// When ScopedObservation is destroyed, it removes the registered observation,
-// if any. Basic example (as a member variable):
+// When a `ScopedObservation` is destroyed, it unregisters the observer from the
+// observable if it was currently observing something. Otherwise it does
+// nothing.
 //
-//   class MyFooObserver : public FooObserver {
-//     ...
+// Using a `ScopedObservation` instead of manually observing and unobserving has
+// the following benefits:
+// - The observer cannot accidentally forget to stop observing when it is
+//   destroyed.
+// - By calling `Reset`, an ongoing observation can be stopped before the
+//   `ScopedObservation` is destroyed. If nothing was currently observed, then
+//   calling `Reset` does nothing. This can be useful for when the observable is
+//   destroyed before the observer is destroyed, because it prevents the
+//   observer from accidentally unregistering itself from the destroyed
+//   observable a second time when it itself is destroyed. Without
+//   `ScopedObservation`, one might need to keep track of whether one has
+//   already stopped observing in a separate boolean.
+//
+// Basic example (as a member variable):
+//
+//   class MyObserver : public Observable::Observer {
+//    public:
+//     MyObserver(Observable* observable) {
+//       observation_.Observe(observable);
+//     }
+//     // Note how there is no need to stop observing in the destructor.
 //    private:
-//     ScopedObservation<Foo, FooObserver> foo_observation_{this};
+//     ScopedObservation<Observable, Observable::Observer> observation_{this};
 //   };
 //
-//   MyFooObserver::MyFooObserver(Foo* foo) {
-//     foo_observation_.Observe(foo);
-//   }
-//
 // For cases with methods not named AddObserver/RemoveObserver:
 //
-//   class MyFooStateObserver : public FooStateObserver {
+//   class MyStateObserver : public Observable::StateObserver {
 //     ...
 //    private:
-//     ScopedObservation<Foo,
-//                       FooStateObserver,
-//                       &Foo::AddStateObserver,
-//                       &Foo::RemoveStateObserver>
-//       foo_observation_{this};
+//     ScopedObservation<Observable,
+//                       Observable::StateObserver,
+//                       &Observable::AddStateObserver,
+//                       &Observable::RemoveStateObserver>
+//       observation_{this};
 //   };
 template <class Source,
           class Observer,
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index d9ccd96f..236a2cb5 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -123,7 +123,7 @@
 }
 
 _BATCH_SUFFIX = '_batch'
-_TEST_BATCH_MAX_GROUP_SIZE = 256
+_TEST_BATCH_MAX_GROUP_SIZE = 200
 
 
 @contextlib.contextmanager
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index a2c3f1d..d8481786 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-9.20220920.3.1
+9.20220921.2.1
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 669587af..d155e43 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1972,8 +1972,11 @@
   if (!can_use_msaa_)
     return 0;
 
-  if (display_list->HasNonAAPaint() && !supports_disable_msaa_)
-    return 0;
+  if (display_list->HasNonAAPaint()) {
+    UMA_HISTOGRAM_BOOLEAN("GPU.SupportsDisableMsaa", supports_disable_msaa_);
+    if (!supports_disable_msaa_)
+      return 0;
+  }
 
   return RequestedMSAASampleCount();
 }
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 4c86d6a..29d34d3 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1418,6 +1418,7 @@
     "javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeUnitTest.java",
     "javatests/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionViewRenderTest.java",
     "javatests/src/org/chromium/chrome/browser/ntp/TitleUtilTest.java",
+    "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java",
     "javatests/src/org/chromium/chrome/browser/tab/WebContentsStateBridgeTest.java",
     "javatests/src/org/chromium/chrome/browser/tab/state/FilePersistedTabDataStorageTest.java",
     "javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index e0b76eb..1a5a9c95 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -461,7 +461,6 @@
   "javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java",
   "javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java",
   "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java",
-  "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java",
   "javatests/src/org/chromium/chrome/browser/storage/BlobUrlStoreTest.java",
   "javatests/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesMetadataUtilsTest.java",
   "javatests/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayoutTest.java",
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.arm64.libs_and_assets.expected b/chrome/android/expectations/trichrome_chrome_bundle.arm64.libs_and_assets.expected
index 8a72eea..cf37af6 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.arm64.libs_and_assets.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.arm64.libs_and_assets.expected
@@ -84,5 +84,5 @@
 apk_path=assets/locales/zh-TW.pak, compress=False, alignment=4
 apk_path=assets/locales/zu.pak, compress=False, alignment=4
 apk_path=assets/resources.pak, compress=False, alignment=4
-apk_path=assets/webapk7.dex, compress=True, alignment=0
+apk_path=assets/webapk8.dex, compress=True, alignment=0
 apk_path=assets/webapk_dex_version.txt, compress=True, alignment=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index 4f499a50..19822bd 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -371,7 +371,7 @@
     }
 
     public void maybeShowWebFeedAwarenessIph() {
-        if (mWebFeedHasContent && mSectionHeaderView.shouldUseWebFeedAwarenessIPH()) {
+        if (mWebFeedHasContent && FeedFeatures.shouldUseWebFeedAwarenessIPH()) {
             UserEducationHelper helper = new UserEducationHelper(mActivity, mHandler);
             mSectionHeaderView.showWebFeedAwarenessIph(
                     helper, StreamTabId.FOLLOWING, new Scroller());
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
index 544ebeb98..bf164bb 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -501,6 +501,12 @@
         if (shouldHaveWebFeedTab) {
             addHeaderAndStream(mContext.getResources().getString(R.string.ntp_following),
                     mCoordinator.createFeedStream(StreamKind.FOLLOWING));
+            if (FeedFeatures.shouldUseNewIndicator()) {
+                mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY)
+                        .get(getTabIdForSection(StreamKind.FOLLOWING))
+                        .set(SectionHeaderProperties.BADGE_TEXT_KEY,
+                                mContext.getResources().getString(R.string.ntp_new));
+            }
         }
     }
 
@@ -585,11 +591,6 @@
         return !mTabToStreamMap.isEmpty();
     }
 
-    boolean isActivityLoggingEnabledForCurrentStream() {
-        if (mCurrentStream == null) return false;
-        return mCurrentStream.isActivityLoggingEnabled();
-    }
-
     long getLastFetchTimeMsForCurrentStream() {
         if (mCurrentStream == null) return 0;
         return mCurrentStream.getLastFetchTimeMs();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 84339e5f..4aa85b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -320,7 +320,7 @@
     public void doneHiding() {
         if (getNextLayout() == getDefaultLayout()) {
             Tab tab = getTabModelSelector() != null ? getTabModelSelector().getCurrentTab() : null;
-            emptyCachesExcept(tab != null ? tab.getId() : Tab.INVALID_TAB_ID);
+            emptyTabCachesExcept(tab != null ? tab.getId() : Tab.INVALID_TAB_ID);
         }
 
         super.doneHiding();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index edbc3f1..c3f3ffcd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -48,6 +48,9 @@
 
     // Internal State
     /** A {@link TitleCache} instance that stores all title/favicon bitmaps as CC resources. */
+    // This cache should not be cleared in LayoutManagerImpl#emptyCachesExcept(), since that method
+    // is currently called when returning to the static layout, which is when these titles will be
+    // visible. See https://crbug.com/1329293.
     protected LayerTitleCache mLayerTitleCache;
 
     private final Supplier<StartSurface> mStartSurfaceSupplier;
@@ -173,12 +176,6 @@
     }
 
     @Override
-    protected void emptyCachesExcept(int tabId) {
-        super.emptyCachesExcept(tabId);
-        if (mLayerTitleCache != null) mLayerTitleCache.clearExcept(tabId);
-    }
-
-    @Override
     public void initLayoutTabFromHost(final int tabId) {
         if (mLayerTitleCache != null) {
             mLayerTitleCache.remove(tabId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
index 9858eda..8d2314e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -1161,7 +1161,7 @@
      * Clears all content associated with {@code tabId} from the internal caches.
      * @param tabId The id of the tab to clear.
      */
-    protected void emptyCachesExcept(int tabId) {
+    protected void emptyTabCachesExcept(int tabId) {
         LayoutTab tab = mTabCache.get(tabId);
         mTabCache.clear();
         if (tab != null) mTabCache.put(tabId, tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index 857c639..8893edc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -589,13 +589,6 @@
             }
 
             @Override
-            public void allTabsClosureCommitted(boolean incognito) {
-                if (mLayerTitleCacheSupplier.hasValue()) {
-                    mLayerTitleCacheSupplier.get().clearExcept(Tab.INVALID_TAB_ID);
-                }
-            }
-
-            @Override
             public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
                 if (tab.getId() == lastId) return;
                 getStripLayoutHelper(tab.isIncognito())
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
index 1b41353..a3eafc7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java
@@ -21,12 +21,12 @@
 import androidx.core.content.res.ResourcesCompat;
 import androidx.test.filters.SmallTest;
 
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.MathUtils;
 import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
@@ -35,13 +35,13 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
-import org.chromium.ui.test.util.BlankUiTestActivity;
 import org.chromium.ui.test.util.BlankUiTestActivityTestCase;
 
 /**
  * Tests for {@link StatusIndicatorViewBinder}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public class StatusIndicatorViewBinderTest extends BlankUiTestActivityTestCase {
     private static final String STATUS_TEXT = "Offline";
 
@@ -52,16 +52,12 @@
     private PropertyModel mModel;
     private PropertyModelChangeProcessor mMCP;
 
-    @BeforeClass
-    public static void setUpBeforeActivityLaunched() {
-        BlankUiTestActivity.setTestLayout(R.layout.status_indicator_container);
-    }
-
     @Override
     public void setUpTest() throws Exception {
         super.setUpTest();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
+            getActivity().setContentView(R.layout.status_indicator_container);
             mContainer = getActivity().findViewById(R.id.status_indicator);
             mStatusTextView = mContainer.findViewById(R.id.status_text);
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 89f5ce3..25ee2c09 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5233,7 +5233,7 @@
       "//chrome/browser/ui/quick_answers",
       "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
       "//chrome/browser/ui/webui/ash/audio:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/cloud_upload:mojo_bindings",
+      "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/emoji:mojo_bindings",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e8efb4d..f4e0fc051 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2201,7 +2201,7 @@
 };
 
 const FeatureEntry::FeatureParam kWebFeedAwareness_new_animation[] = {
-    {"awareness_style", "new animation"}};
+    {"awareness_style", "new_animation"}};
 
 const FeatureEntry::FeatureParam kWebFeedAwareness_IPH[] = {
     {"awareness_style", "IPH"}};
@@ -7614,6 +7614,11 @@
      flag_descriptions::kAttributionReportingDebugModeDescription, kOsAll,
      SINGLE_VALUE_TYPE(switches::kAttributionReportingDebugMode)},
 
+    {"private-aggregation-debug-mode",
+     flag_descriptions::kPrivateAggregationDebugModeName,
+     flag_descriptions::kPrivateAggregationDebugModeDescription, kOsAll,
+     SINGLE_VALUE_TYPE(switches::kPrivateAggregationDebugMode)},
+
     {"client-storage-access-context-auditing",
      flag_descriptions::kClientStorageAccessContextAuditingName,
      flag_descriptions::kClientStorageAccessContextAuditingDescription, kOsAll,
diff --git a/chrome/browser/apps/app_preload_service/device_info_manager.cc b/chrome/browser/apps/app_preload_service/device_info_manager.cc
index a27f55e5..53e105a 100644
--- a/chrome/browser/apps/app_preload_service/device_info_manager.cc
+++ b/chrome/browser/apps/app_preload_service/device_info_manager.cc
@@ -24,8 +24,9 @@
 }
 
 std::string DeviceInfoManager::GetChromeOsPlatformVersion() const {
-  return chromeos::version_loader::GetVersion(
+  absl::optional<std::string> version = chromeos::version_loader::GetVersion(
       chromeos::version_loader::VERSION_SHORT);
+  return version.value_or("0.0.0.0");
 }
 
 std::string DeviceInfoManager::GetUserType() const {
diff --git a/chrome/browser/apps/app_service/browser_app_instance_registry.cc b/chrome/browser/apps/app_service/browser_app_instance_registry.cc
index 437c14f..e7577da 100644
--- a/chrome/browser/apps/app_service/browser_app_instance_registry.cc
+++ b/chrome/browser/apps/app_service/browser_app_instance_registry.cc
@@ -59,6 +59,20 @@
   });
 }
 
+aura::Window* BrowserAppInstanceRegistry::GetWindowByInstanceId(
+    const base::UnguessableToken& id) const {
+  if (const BrowserAppInstance* instance = GetAppInstanceById(id)) {
+    return instance->window;
+  }
+
+  if (const BrowserWindowInstance* instance =
+          GetBrowserWindowInstanceById(id)) {
+    return instance->window;
+  }
+
+  return nullptr;
+}
+
 std::set<const BrowserWindowInstance*>
 BrowserAppInstanceRegistry::GetLacrosBrowserWindowInstances() const {
   std::set<const BrowserWindowInstance*> result;
@@ -109,14 +123,8 @@
 
 void BrowserAppInstanceRegistry::MinimizeInstance(
     const base::UnguessableToken& id) {
-  if (const BrowserAppInstance* instance = GetAppInstanceById(id)) {
-    MinimizeWindow(instance->window);
-    return;
-  }
-
-  if (const BrowserWindowInstance* instance =
-          GetBrowserWindowInstanceById(id)) {
-    MinimizeWindow(instance->window);
+  if (aura::Window* window = GetWindowByInstanceId(id)) {
+    MinimizeWindow(window);
   }
 }
 
diff --git a/chrome/browser/apps/app_service/browser_app_instance_registry.h b/chrome/browser/apps/app_service/browser_app_instance_registry.h
index 59cd1f8a..4c4b7c91 100644
--- a/chrome/browser/apps/app_service/browser_app_instance_registry.h
+++ b/chrome/browser/apps/app_service/browser_app_instance_registry.h
@@ -57,6 +57,10 @@
   const BrowserWindowInstance* GetBrowserWindowInstanceById(
       base::UnguessableToken id) const;
 
+  // Get a single window by instance ID (Ash or Lacros). Returns a nullptr if
+  // instance identified by |id| does not exist.
+  aura::Window* GetWindowByInstanceId(const base::UnguessableToken& id) const;
+
   // Get all instances of lacros browser window instances.
   std::set<const BrowserWindowInstance*> GetLacrosBrowserWindowInstances()
       const;
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
index 6fe4a6bc2..219cffff 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
@@ -163,8 +163,7 @@
                                    LaunchSource launch_source,
                                    WindowInfoPtr window_info) {
   DCHECK_EQ(app_constants::kLacrosAppId, app_id);
-  crosapi::BrowserManager::Get()->NewTab(
-      /*should_trigger_session_restore=*/true);
+  crosapi::BrowserManager::Get()->Launch();
 }
 
 void StandaloneBrowserApps::LaunchAppWithParams(AppLaunchParams&& params,
@@ -205,8 +204,7 @@
                                    apps::mojom::LaunchSource launch_source,
                                    apps::mojom::WindowInfoPtr window_info) {
   DCHECK_EQ(app_constants::kLacrosAppId, app_id);
-  crosapi::BrowserManager::Get()->NewTab(
-      /*should_trigger_session_restore=*/true);
+  crosapi::BrowserManager::Get()->Launch();
 }
 
 void StandaloneBrowserApps::GetMenuModel(const std::string& app_id,
diff --git a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
index f25b85a..5d49816 100644
--- a/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
+++ b/chrome/browser/apps/app_shim/app_shim_manager_mac.cc
@@ -1137,6 +1137,8 @@
   // Update the application dock menu for the current profile.
   const std::string app_id =
       web_app::GetAppIdFromApplicationName(browser->app_name());
+  if (!delegate_->AppUsesRemoteCocoa(browser->profile(), app_id))
+    return;
   auto* profile_state = GetOrCreateProfileState(browser->profile(), app_id);
   if (profile_state)
     UpdateApplicationDockMenu(browser->profile(), profile_state);
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index e2898ce..7c258c5 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -1026,15 +1026,18 @@
   TestHelper("testFocusRestored", "web_view/focus", NO_TEST_SERVER);
   content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
   ASSERT_TRUE(embedder_web_contents);
-  ASSERT_TRUE(DeprecatedGuestWebContents());
+  content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost();
+  ASSERT_TRUE(guest_rfh);
 
   // 1) We click on the guest so that we get a focus event.
   ExtensionTestMessageListener next_step_listener("TEST_STEP_PASSED");
   next_step_listener.set_failure_message("TEST_STEP_FAILED");
   {
-    content::SimulateMouseClickAt(DeprecatedGuestWebContents(), 0,
-                                  blink::WebMouseEvent::Button::kLeft,
-                                  gfx::Point(10, 10));
+    WaitForHitTestData(guest_rfh);
+    content::SimulateMouseClickAt(
+        embedder_web_contents, 0, blink::WebMouseEvent::Button::kLeft,
+        guest_rfh->GetView()->TransformPointToRootCoordSpace(
+            gfx::Point(10, 10)));
     EXPECT_TRUE(content::ExecuteScript(
                     embedder_web_contents,
                     "window.runCommand('testFocusRestoredRunNextStep', 1);"));
@@ -1060,9 +1063,11 @@
   // input element, then we ensure text_input_type is properly set.
   next_step_listener.Reset();
   {
-    content::SimulateMouseClickAt(DeprecatedGuestWebContents(), 0,
-                                  blink::WebMouseEvent::Button::kLeft,
-                                  gfx::Point(10, 10));
+    WaitForHitTestData(guest_rfh);
+    content::SimulateMouseClickAt(
+        embedder_web_contents, 0, blink::WebMouseEvent::Button::kLeft,
+        guest_rfh->GetView()->TransformPointToRootCoordSpace(
+            gfx::Point(10, 10)));
     EXPECT_TRUE(content::ExecuteScript(
                     embedder_web_contents,
                     "window.runCommand('testFocusRestoredRunNextStep', 3)"));
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index ed6e222..2608562 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -10,6 +10,7 @@
 import("//extensions/buildflags/buildflags.gni")
 import("//media/media_options.gni")
 import("//printing/buildflags/buildflags.gni")
+import("//rlz/buildflags/buildflags.gni")
 import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/libprotobuf-mutator/fuzzable_proto_library.gni")
 import("//third_party/protobuf/proto_library.gni")
@@ -1358,6 +1359,471 @@
     "lock_screen_apps/toast_dialog_view.h",
     "logging.cc",
     "logging.h",
+    "login/active_directory_migration_utils.cc",
+    "login/active_directory_migration_utils.h",
+    "login/app_mode/kiosk_launch_controller.cc",
+    "login/app_mode/kiosk_launch_controller.h",
+    "login/auth/chrome_cryptohome_authenticator.cc",
+    "login/auth/chrome_cryptohome_authenticator.h",
+    "login/auth/chrome_login_performer.cc",
+    "login/auth/chrome_login_performer.h",
+    "login/auth/chrome_safe_mode_delegate.cc",
+    "login/auth/chrome_safe_mode_delegate.h",
+    "login/challenge_response_auth_keys_loader.cc",
+    "login/challenge_response_auth_keys_loader.h",
+    "login/chrome_restart_request.cc",
+    "login/chrome_restart_request.h",
+    "login/configuration_keys.cc",
+    "login/configuration_keys.h",
+    "login/consolidated_consent_field_trial.cc",
+    "login/consolidated_consent_field_trial.h",
+    "login/demo_mode/demo_extensions_external_loader.cc",
+    "login/demo_mode/demo_extensions_external_loader.h",
+    "login/demo_mode/demo_mode_resources_remover.cc",
+    "login/demo_mode/demo_mode_resources_remover.h",
+    "login/demo_mode/demo_resources.cc",
+    "login/demo_mode/demo_resources.h",
+    "login/demo_mode/demo_session.cc",
+    "login/demo_mode/demo_session.h",
+    "login/demo_mode/demo_setup_controller.cc",
+    "login/demo_mode/demo_setup_controller.h",
+    "login/easy_unlock/chrome_proximity_auth_client.cc",
+    "login/easy_unlock/chrome_proximity_auth_client.h",
+    "login/easy_unlock/easy_unlock_auth_attempt.cc",
+    "login/easy_unlock/easy_unlock_auth_attempt.h",
+    "login/easy_unlock/easy_unlock_challenge_wrapper.cc",
+    "login/easy_unlock/easy_unlock_challenge_wrapper.h",
+    "login/easy_unlock/easy_unlock_create_keys_operation.cc",
+    "login/easy_unlock/easy_unlock_create_keys_operation.h",
+    "login/easy_unlock/easy_unlock_get_keys_operation.cc",
+    "login/easy_unlock/easy_unlock_get_keys_operation.h",
+    "login/easy_unlock/easy_unlock_key_manager.cc",
+    "login/easy_unlock/easy_unlock_key_manager.h",
+    "login/easy_unlock/easy_unlock_key_names.cc",
+    "login/easy_unlock/easy_unlock_key_names.h",
+    "login/easy_unlock/easy_unlock_metrics.cc",
+    "login/easy_unlock/easy_unlock_metrics.h",
+    "login/easy_unlock/easy_unlock_notification_controller.cc",
+    "login/easy_unlock/easy_unlock_notification_controller.h",
+    "login/easy_unlock/easy_unlock_refresh_keys_operation.cc",
+    "login/easy_unlock/easy_unlock_refresh_keys_operation.h",
+    "login/easy_unlock/easy_unlock_remove_keys_operation.cc",
+    "login/easy_unlock/easy_unlock_remove_keys_operation.h",
+    "login/easy_unlock/easy_unlock_service.cc",
+    "login/easy_unlock/easy_unlock_service.h",
+    "login/easy_unlock/easy_unlock_service_factory.cc",
+    "login/easy_unlock/easy_unlock_service_factory.h",
+    "login/easy_unlock/easy_unlock_service_regular.cc",
+    "login/easy_unlock/easy_unlock_service_regular.h",
+    "login/easy_unlock/easy_unlock_service_signin.cc",
+    "login/easy_unlock/easy_unlock_service_signin.h",
+    "login/easy_unlock/easy_unlock_tpm_key_manager.cc",
+    "login/easy_unlock/easy_unlock_tpm_key_manager.h",
+    "login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc",
+    "login/easy_unlock/easy_unlock_tpm_key_manager_factory.h",
+    "login/easy_unlock/easy_unlock_types.cc",
+    "login/easy_unlock/easy_unlock_types.h",
+    "login/easy_unlock/easy_unlock_user_login_flow.cc",
+    "login/easy_unlock/easy_unlock_user_login_flow.h",
+    "login/easy_unlock/smartlock_feature_usage_metrics.cc",
+    "login/easy_unlock/smartlock_feature_usage_metrics.h",
+    "login/easy_unlock/smartlock_state_handler.cc",
+    "login/easy_unlock/smartlock_state_handler.h",
+    "login/enrollment/auto_enrollment_check_screen.cc",
+    "login/enrollment/auto_enrollment_check_screen.h",
+    "login/enrollment/auto_enrollment_check_screen_view.h",
+    "login/enrollment/auto_enrollment_controller.cc",
+    "login/enrollment/auto_enrollment_controller.h",
+    "login/enrollment/enrollment_screen.cc",
+    "login/enrollment/enrollment_screen.h",
+    "login/enrollment/enrollment_screen_view.h",
+    "login/enrollment/enrollment_uma.cc",
+    "login/enrollment/enrollment_uma.h",
+    "login/enrollment/enterprise_enrollment_helper.cc",
+    "login/enrollment/enterprise_enrollment_helper.h",
+    "login/enrollment/enterprise_enrollment_helper_impl.cc",
+    "login/enrollment/enterprise_enrollment_helper_impl.h",
+    "login/enterprise_user_session_metrics.cc",
+    "login/enterprise_user_session_metrics.h",
+    "login/error_screens_histogram_helper.cc",
+    "login/error_screens_histogram_helper.h",
+    "login/existing_user_controller.cc",
+    "login/existing_user_controller.h",
+    "login/extensions/login_screen_extensions_content_script_manager.cc",
+    "login/extensions/login_screen_extensions_content_script_manager.h",
+    "login/extensions/login_screen_extensions_content_script_manager_factory.cc",
+    "login/extensions/login_screen_extensions_content_script_manager_factory.h",
+    "login/extensions/login_screen_extensions_lifetime_manager.cc",
+    "login/extensions/login_screen_extensions_lifetime_manager.h",
+    "login/extensions/login_screen_extensions_lifetime_manager_factory.cc",
+    "login/extensions/login_screen_extensions_lifetime_manager_factory.h",
+    "login/gaia_reauth_token_fetcher.cc",
+    "login/gaia_reauth_token_fetcher.h",
+    "login/hats_unlock_survey_trigger.cc",
+    "login/hats_unlock_survey_trigger.h",
+    "login/help_app_launcher.cc",
+    "login/help_app_launcher.h",
+    "login/helper.cc",
+    "login/helper.h",
+    "login/hwid_checker.cc",
+    "login/hwid_checker.h",
+    "login/lock/screen_locker.cc",
+    "login/lock/screen_locker.h",
+    "login/lock/views_screen_locker.cc",
+    "login/lock/views_screen_locker.h",
+    "login/lock_screen_utils.cc",
+    "login/lock_screen_utils.h",
+    "login/login_auth_recorder.cc",
+    "login/login_auth_recorder.h",
+    "login/login_client_cert_usage_observer.cc",
+    "login/login_client_cert_usage_observer.h",
+    "login/login_pref_names.cc",
+    "login/login_pref_names.h",
+    "login/login_screen_extensions_storage_cleaner.cc",
+    "login/login_screen_extensions_storage_cleaner.h",
+    "login/login_wizard.h",
+    "login/marketing_backend_connector.cc",
+    "login/marketing_backend_connector.h",
+    "login/mojo_system_info_dispatcher.cc",
+    "login/mojo_system_info_dispatcher.h",
+    "login/onboarding_user_activity_counter.cc",
+    "login/onboarding_user_activity_counter.h",
+    "login/oobe_configuration.cc",
+    "login/oobe_configuration.h",
+    "login/oobe_screen.cc",
+    "login/oobe_screen.h",
+    "login/profile_auth_data.cc",
+    "login/profile_auth_data.h",
+    "login/quick_unlock/auth_token.cc",
+    "login/quick_unlock/auth_token.h",
+    "login/quick_unlock/fake_pin_salt_storage.cc",
+    "login/quick_unlock/fake_pin_salt_storage.h",
+    "login/quick_unlock/fingerprint_power_button_race_detector.cc",
+    "login/quick_unlock/fingerprint_power_button_race_detector.h",
+    "login/quick_unlock/fingerprint_storage.cc",
+    "login/quick_unlock/fingerprint_storage.h",
+    "login/quick_unlock/fingerprint_utils.cc",
+    "login/quick_unlock/fingerprint_utils.h",
+    "login/quick_unlock/pin_backend.cc",
+    "login/quick_unlock/pin_backend.h",
+    "login/quick_unlock/pin_salt_storage.cc",
+    "login/quick_unlock/pin_salt_storage.h",
+    "login/quick_unlock/pin_storage_cryptohome.cc",
+    "login/quick_unlock/pin_storage_cryptohome.h",
+    "login/quick_unlock/pin_storage_prefs.cc",
+    "login/quick_unlock/pin_storage_prefs.h",
+    "login/quick_unlock/quick_unlock_factory.cc",
+    "login/quick_unlock/quick_unlock_factory.h",
+    "login/quick_unlock/quick_unlock_storage.cc",
+    "login/quick_unlock/quick_unlock_storage.h",
+    "login/quick_unlock/quick_unlock_utils.cc",
+    "login/quick_unlock/quick_unlock_utils.h",
+    "login/reauth_stats.cc",
+    "login/reauth_stats.h",
+    "login/reporting/login_logout_reporter.cc",
+    "login/reporting/login_logout_reporter.h",
+    "login/saml/in_session_password_change_manager.cc",
+    "login/saml/in_session_password_change_manager.h",
+    "login/saml/in_session_password_sync_manager.cc",
+    "login/saml/in_session_password_sync_manager.h",
+    "login/saml/in_session_password_sync_manager_factory.cc",
+    "login/saml/in_session_password_sync_manager_factory.h",
+    "login/saml/password_change_success_notification.cc",
+    "login/saml/password_change_success_notification.h",
+    "login/saml/password_expiry_notification.cc",
+    "login/saml/password_expiry_notification.h",
+    "login/saml/password_sync_token_checkers_collection.cc",
+    "login/saml/password_sync_token_checkers_collection.h",
+    "login/saml/password_sync_token_fetcher.cc",
+    "login/saml/password_sync_token_fetcher.h",
+    "login/saml/password_sync_token_login_checker.cc",
+    "login/saml/password_sync_token_login_checker.h",
+    "login/saml/password_sync_token_verifier.cc",
+    "login/saml/password_sync_token_verifier.h",
+    "login/saml/password_sync_token_verifier_factory.cc",
+    "login/saml/password_sync_token_verifier_factory.h",
+    "login/saml/public_saml_url_fetcher.cc",
+    "login/saml/public_saml_url_fetcher.h",
+    "login/saml/saml_metric_utils.cc",
+    "login/saml/saml_metric_utils.h",
+    "login/saml/saml_profile_prefs.cc",
+    "login/saml/saml_profile_prefs.h",
+    "login/screen_manager.cc",
+    "login/screen_manager.h",
+    "login/screens/active_directory_login_screen.cc",
+    "login/screens/active_directory_login_screen.h",
+    "login/screens/active_directory_password_change_screen.cc",
+    "login/screens/active_directory_password_change_screen.h",
+    "login/screens/app_downloading_screen.cc",
+    "login/screens/app_downloading_screen.h",
+    "login/screens/arc_terms_of_service_screen.cc",
+    "login/screens/arc_terms_of_service_screen.h",
+    "login/screens/assistant_optin_flow_screen.cc",
+    "login/screens/assistant_optin_flow_screen.h",
+    "login/screens/base_screen.cc",
+    "login/screens/base_screen.h",
+    "login/screens/chrome_user_selection_screen.cc",
+    "login/screens/chrome_user_selection_screen.h",
+    "login/screens/chromevox_hint/chromevox_hint_detector.cc",
+    "login/screens/chromevox_hint/chromevox_hint_detector.h",
+    "login/screens/consolidated_consent_screen.cc",
+    "login/screens/consolidated_consent_screen.h",
+    "login/screens/cryptohome_recovery_screen.cc",
+    "login/screens/cryptohome_recovery_screen.h",
+    "login/screens/demo_preferences_screen.cc",
+    "login/screens/demo_preferences_screen.h",
+    "login/screens/demo_setup_screen.cc",
+    "login/screens/demo_setup_screen.h",
+    "login/screens/device_disabled_screen.cc",
+    "login/screens/device_disabled_screen.h",
+    "login/screens/edu_coexistence_login_screen.cc",
+    "login/screens/edu_coexistence_login_screen.h",
+    "login/screens/enable_adb_sideloading_screen.cc",
+    "login/screens/enable_adb_sideloading_screen.h",
+    "login/screens/enable_debugging_screen.cc",
+    "login/screens/enable_debugging_screen.h",
+    "login/screens/encryption_migration_mode.h",
+    "login/screens/encryption_migration_screen.cc",
+    "login/screens/encryption_migration_screen.h",
+    "login/screens/error_screen.cc",
+    "login/screens/error_screen.h",
+    "login/screens/eula_screen.cc",
+    "login/screens/eula_screen.h",
+    "login/screens/family_link_notice_screen.cc",
+    "login/screens/family_link_notice_screen.h",
+    "login/screens/fingerprint_setup_screen.cc",
+    "login/screens/fingerprint_setup_screen.h",
+    "login/screens/gaia_password_changed_screen.cc",
+    "login/screens/gaia_password_changed_screen.h",
+    "login/screens/gaia_screen.cc",
+    "login/screens/gaia_screen.h",
+    "login/screens/gesture_navigation_screen.cc",
+    "login/screens/gesture_navigation_screen.h",
+    "login/screens/guest_tos_screen.cc",
+    "login/screens/guest_tos_screen.h",
+    "login/screens/hardware_data_collection_screen.cc",
+    "login/screens/hardware_data_collection_screen.h",
+    "login/screens/hid_detection_screen.cc",
+    "login/screens/hid_detection_screen.h",
+    "login/screens/kiosk_autolaunch_screen.cc",
+    "login/screens/kiosk_autolaunch_screen.h",
+    "login/screens/kiosk_enable_screen.cc",
+    "login/screens/kiosk_enable_screen.h",
+    "login/screens/lacros_data_backward_migration_screen.cc",
+    "login/screens/lacros_data_backward_migration_screen.h",
+    "login/screens/lacros_data_migration_screen.cc",
+    "login/screens/lacros_data_migration_screen.h",
+    "login/screens/local_state_error_screen.cc",
+    "login/screens/local_state_error_screen.h",
+    "login/screens/locale_switch_screen.cc",
+    "login/screens/locale_switch_screen.h",
+    "login/screens/management_transition_screen.cc",
+    "login/screens/management_transition_screen.h",
+    "login/screens/marketing_opt_in_screen.cc",
+    "login/screens/marketing_opt_in_screen.h",
+    "login/screens/multidevice_setup_screen.cc",
+    "login/screens/multidevice_setup_screen.h",
+    "login/screens/network_error.cc",
+    "login/screens/network_error.h",
+    "login/screens/network_screen.cc",
+    "login/screens/network_screen.h",
+    "login/screens/offline_login_screen.cc",
+    "login/screens/offline_login_screen.h",
+    "login/screens/os_install_screen.cc",
+    "login/screens/os_install_screen.h",
+    "login/screens/os_trial_screen.cc",
+    "login/screens/os_trial_screen.h",
+    "login/screens/packaged_license_screen.cc",
+    "login/screens/packaged_license_screen.h",
+    "login/screens/parental_handoff_screen.cc",
+    "login/screens/parental_handoff_screen.h",
+    "login/screens/pin_setup_screen.cc",
+    "login/screens/pin_setup_screen.h",
+    "login/screens/quick_start_screen.cc",
+    "login/screens/quick_start_screen.h",
+    "login/screens/recommend_apps/fake_recommend_apps_fetcher.cc",
+    "login/screens/recommend_apps/fake_recommend_apps_fetcher.h",
+    "login/screens/recommend_apps/recommend_apps_fetcher.cc",
+    "login/screens/recommend_apps/recommend_apps_fetcher.h",
+    "login/screens/recommend_apps/recommend_apps_fetcher_delegate.h",
+    "login/screens/recommend_apps/recommend_apps_fetcher_impl.cc",
+    "login/screens/recommend_apps/recommend_apps_fetcher_impl.h",
+    "login/screens/recommend_apps_screen.cc",
+    "login/screens/recommend_apps_screen.h",
+    "login/screens/reset_screen.cc",
+    "login/screens/reset_screen.h",
+    "login/screens/saml_confirm_password_screen.cc",
+    "login/screens/saml_confirm_password_screen.h",
+    "login/screens/signin_fatal_error_screen.cc",
+    "login/screens/signin_fatal_error_screen.h",
+    "login/screens/smart_privacy_protection_screen.cc",
+    "login/screens/smart_privacy_protection_screen.h",
+    "login/screens/sync_consent_screen.cc",
+    "login/screens/sync_consent_screen.h",
+    "login/screens/terms_of_service_screen.cc",
+    "login/screens/terms_of_service_screen.h",
+    "login/screens/theme_selection_screen.cc",
+    "login/screens/theme_selection_screen.h",
+    "login/screens/tpm_error_screen.cc",
+    "login/screens/tpm_error_screen.h",
+    "login/screens/update_required_screen.cc",
+    "login/screens/update_required_screen.h",
+    "login/screens/update_screen.cc",
+    "login/screens/update_screen.h",
+    "login/screens/user_creation_screen.cc",
+    "login/screens/user_creation_screen.h",
+    "login/screens/user_selection_screen.cc",
+    "login/screens/user_selection_screen.h",
+    "login/screens/welcome_screen.cc",
+    "login/screens/welcome_screen.h",
+    "login/screens/wrong_hwid_screen.cc",
+    "login/screens/wrong_hwid_screen.h",
+    "login/security_token_pin_dialog_host_login_impl.cc",
+    "login/security_token_pin_dialog_host_login_impl.h",
+    "login/security_token_session_controller.cc",
+    "login/security_token_session_controller.h",
+    "login/security_token_session_controller_factory.cc",
+    "login/security_token_session_controller_factory.h",
+    "login/session/chrome_session_manager.cc",
+    "login/session/chrome_session_manager.h",
+    "login/session/user_session_initializer.cc",
+    "login/session/user_session_initializer.h",
+    "login/session/user_session_manager.cc",
+    "login/session/user_session_manager.h",
+    "login/signin/auth_error_observer.cc",
+    "login/signin/auth_error_observer.h",
+    "login/signin/auth_error_observer_factory.cc",
+    "login/signin/auth_error_observer_factory.h",
+    "login/signin/merge_session_navigation_throttle.cc",
+    "login/signin/merge_session_navigation_throttle.h",
+    "login/signin/merge_session_throttling_utils.cc",
+    "login/signin/merge_session_throttling_utils.h",
+    "login/signin/oauth2_login_manager.cc",
+    "login/signin/oauth2_login_manager.h",
+    "login/signin/oauth2_login_manager_factory.cc",
+    "login/signin/oauth2_login_manager_factory.h",
+    "login/signin/oauth2_login_verifier.cc",
+    "login/signin/oauth2_login_verifier.h",
+    "login/signin/oauth2_token_fetcher.cc",
+    "login/signin/oauth2_token_fetcher.h",
+    "login/signin/oauth2_token_initializer.cc",
+    "login/signin/oauth2_token_initializer.h",
+    "login/signin/offline_signin_limiter.cc",
+    "login/signin/offline_signin_limiter.h",
+    "login/signin/offline_signin_limiter_factory.cc",
+    "login/signin/offline_signin_limiter_factory.h",
+    "login/signin/signin_error_notifier.cc",
+    "login/signin/signin_error_notifier.h",
+    "login/signin/signin_error_notifier_factory.cc",
+    "login/signin/signin_error_notifier_factory.h",
+    "login/signin/token_handle_fetcher.cc",
+    "login/signin/token_handle_fetcher.h",
+    "login/signin/token_handle_util.cc",
+    "login/signin/token_handle_util.h",
+    "login/signin_partition_manager.cc",
+    "login/signin_partition_manager.h",
+    "login/signin_specifics.h",
+    "login/startup_utils.cc",
+    "login/startup_utils.h",
+    "login/ui/captive_portal_dialog_delegate.cc",
+    "login/ui/captive_portal_dialog_delegate.h",
+    "login/ui/captive_portal_view.cc",
+    "login/ui/captive_portal_view.h",
+    "login/ui/captive_portal_window_proxy.cc",
+    "login/ui/captive_portal_window_proxy.h",
+    "login/ui/input_events_blocker.cc",
+    "login/ui/input_events_blocker.h",
+    "login/ui/kiosk_app_menu_controller.cc",
+    "login/ui/kiosk_app_menu_controller.h",
+    "login/ui/login_display.cc",
+    "login/ui/login_display.h",
+    "login/ui/login_display_host.cc",
+    "login/ui/login_display_host.h",
+    "login/ui/login_display_host_common.cc",
+    "login/ui/login_display_host_common.h",
+    "login/ui/login_display_host_mojo.cc",
+    "login/ui/login_display_host_mojo.h",
+    "login/ui/login_display_host_webui.cc",
+    "login/ui/login_display_host_webui.h",
+    "login/ui/login_display_mojo.cc",
+    "login/ui/login_display_mojo.h",
+    "login/ui/login_display_webui.cc",
+    "login/ui/login_display_webui.h",
+    "login/ui/login_feedback.cc",
+    "login/ui/login_feedback.h",
+    "login/ui/login_screen_extension_ui/create_options.cc",
+    "login/ui/login_screen_extension_ui/create_options.h",
+    "login/ui/login_screen_extension_ui/dialog_delegate.cc",
+    "login/ui/login_screen_extension_ui/dialog_delegate.h",
+    "login/ui/login_screen_extension_ui/web_dialog_view.cc",
+    "login/ui/login_screen_extension_ui/web_dialog_view.h",
+    "login/ui/login_screen_extension_ui/window.cc",
+    "login/ui/login_screen_extension_ui/window.h",
+    "login/ui/login_web_dialog.cc",
+    "login/ui/login_web_dialog.h",
+    "login/ui/oobe_dialog_size_utils.cc",
+    "login/ui/oobe_dialog_size_utils.h",
+    "login/ui/oobe_ui_dialog_delegate.cc",
+    "login/ui/oobe_ui_dialog_delegate.h",
+    "login/ui/signin_ui.h",
+    "login/ui/simple_web_view_dialog.cc",
+    "login/ui/simple_web_view_dialog.h",
+    "login/ui/user_adding_screen.cc",
+    "login/ui/user_adding_screen.h",
+    "login/ui/user_adding_screen_input_methods_controller.cc",
+    "login/ui/user_adding_screen_input_methods_controller.h",
+    "login/ui/views/user_board_view.h",
+    "login/ui/web_contents_forced_title.cc",
+    "login/ui/web_contents_forced_title.h",
+    "login/ui/webui_login_view.cc",
+    "login/ui/webui_login_view.h",
+    "login/user_board_view_mojo.cc",
+    "login/user_board_view_mojo.h",
+    "login/user_flow.cc",
+    "login/user_flow.h",
+    "login/user_online_signin_notifier.cc",
+    "login/user_online_signin_notifier.h",
+    "login/users/affiliation.cc",
+    "login/users/affiliation.h",
+    "login/users/avatar/user_image_file_selector.cc",
+    "login/users/avatar/user_image_file_selector.h",
+    "login/users/avatar/user_image_loader.cc",
+    "login/users/avatar/user_image_loader.h",
+    "login/users/avatar/user_image_manager.cc",
+    "login/users/avatar/user_image_manager.h",
+    "login/users/avatar/user_image_manager_impl.cc",
+    "login/users/avatar/user_image_manager_impl.h",
+    "login/users/avatar/user_image_sync_observer.cc",
+    "login/users/avatar/user_image_sync_observer.h",
+    "login/users/chrome_user_manager.cc",
+    "login/users/chrome_user_manager.h",
+    "login/users/chrome_user_manager_impl.cc",
+    "login/users/chrome_user_manager_impl.h",
+    "login/users/chrome_user_manager_util.cc",
+    "login/users/chrome_user_manager_util.h",
+    "login/users/default_user_image/default_user_images.cc",
+    "login/users/default_user_image/default_user_images.h",
+    "login/users/multi_profile_user_controller.cc",
+    "login/users/multi_profile_user_controller.h",
+    "login/users/multi_profile_user_controller_delegate.h",
+    "login/users/scoped_test_user_manager.cc",
+    "login/users/scoped_test_user_manager.h",
+    "login/users/supervised_user_manager.h",
+    "login/users/supervised_user_manager_impl.cc",
+    "login/users/supervised_user_manager_impl.h",
+    "login/users/test_users.cc",
+    "login/users/test_users.h",
+    "login/users/user_manager_interface.h",
+    "login/version_info_updater.cc",
+    "login/version_info_updater.h",
+    "login/version_updater/update_time_estimator.cc",
+    "login/version_updater/update_time_estimator.h",
+    "login/version_updater/version_updater.cc",
+    "login/version_updater/version_updater.h",
+    "login/wizard_context.cc",
+    "login/wizard_context.h",
+    "login/wizard_controller.cc",
+    "login/wizard_controller.h",
     "mobile/mobile_activator.cc",
     "mobile/mobile_activator.h",
     "multidevice_setup/auth_token_validator_factory.cc",
@@ -2548,6 +3014,8 @@
   ]
 
   public_deps = [
+    ":device_configuration_proto",
+    ":login_logout_event_proto",
     ":print_job_info_proto",
     ":screen_brightness_event_proto",
     ":user_activity_event_proto",
@@ -2567,6 +3035,7 @@
     "//ash/components/phonehub",
     "//ash/components/phonehub/proto",
     "//ash/components/policy",
+    "//ash/components/proximity_auth",
     "//ash/components/settings",
     "//ash/components/smbfs",
     "//ash/components/tether",
@@ -2615,6 +3084,7 @@
     "//chrome/browser/ash/crosapi:browser_util",
     "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
     "//chrome/browser/ash/guest_os:guest_os_diagnostics_mojom",
+    "//chrome/browser/ash/login/oobe_quick_start",
     "//chrome/browser/ash/power/ml/smart_dim",
     "//chrome/browser/ash/system_web_apps/types",
     "//chrome/browser/chromeos",
@@ -2640,6 +3110,7 @@
     "//chromeos/ash/components/attestation",
     "//chromeos/ash/components/audio",
     "//chromeos/ash/components/browser_context_helper",
+    "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus:metrics_event_proto",
     "//chromeos/ash/components/dbus:vm_applications_apps_proto",
     "//chromeos/ash/components/dbus:vm_launch_proto",
@@ -2656,6 +3127,7 @@
     "//chromeos/ash/components/dbus/concierge:concierge_proto",
     "//chromeos/ash/components/dbus/constants",
     "//chromeos/ash/components/dbus/cros_disks",
+    "//chromeos/ash/components/dbus/cryptohome:cryptohome_proto",
     "//chromeos/ash/components/dbus/debug_daemon",
     "//chromeos/ash/components/dbus/dlcservice",
     "//chromeos/ash/components/dbus/fusebox:proto",
@@ -2664,6 +3136,7 @@
     "//chromeos/ash/components/dbus/kerberos:kerberos_proto",
     "//chromeos/ash/components/dbus/lorgnette_manager",
     "//chromeos/ash/components/dbus/lorgnette_manager:lorgnette_proto",
+    "//chromeos/ash/components/dbus/os_install",
     "//chromeos/ash/components/dbus/resourced",
     "//chromeos/ash/components/dbus/seneschal",
     "//chromeos/ash/components/dbus/seneschal:seneschal_proto",
@@ -2682,11 +3155,17 @@
     "//chromeos/ash/components/drivefs",
     "//chromeos/ash/components/drivefs/mojom",
     "//chromeos/ash/components/enhanced_network_tts/mojom",
+    "//chromeos/ash/components/feature_usage",
     "//chromeos/ash/components/geolocation",
+    "//chromeos/ash/components/hid_detection",
     "//chromeos/ash/components/install_attributes",
     "//chromeos/ash/components/login/auth",
+    "//chromeos/ash/components/login/auth/public:authpublic",
+    "//chromeos/ash/components/login/auth/public:challenge_response_key",
     "//chromeos/ash/components/login/session",
     "//chromeos/ash/components/memory",
+    "//chromeos/ash/components/mojo_service_manager/mojom",
+    "//chromeos/ash/components/multidevice",
     "//chromeos/ash/components/network",
     "//chromeos/ash/components/network/portal_detector",
     "//chromeos/ash/components/scanning",
@@ -2694,7 +3173,6 @@
     "//chromeos/ash/services/cros_healthd/public/cpp",
     "//chromeos/ash/services/cros_healthd/public/mojom",
     "//chromeos/ash/services/rollback_network_config/public/mojom",
-    "//chromeos/components/mojo_service_manager/mojom",
     "//chromeos/components/onc",
     "//chromeos/components/remote_apps/mojom",
     "//chromeos/components/sensors:buildflags",
@@ -2736,9 +3214,11 @@
     "//components/guest_os",
     "//components/history/core/browser",
     "//components/invalidation/public",
+    "//components/keep_alive_registry",
     "//components/keyed_service/content",
     "//components/keyed_service/core",
     "//components/leveldb_proto",
+    "//components/login",
     "//components/metrics",
     "//components/onc",
     "//components/ownership",
@@ -2769,6 +3249,7 @@
     "//components/services/app_service/public/mojom:types_headers",
     "//components/services/unzip/public/cpp",
     "//components/session_manager/core",
+    "//components/signin/core/browser",
     "//components/signin/public/identity_manager",
     "//components/soda",
     "//components/storage_monitor",
@@ -2777,6 +3258,7 @@
     "//components/sync/protocol",
     "//components/sync_preferences",
     "//components/user_manager",
+    "//components/version_info",
     "//components/viz/common",
     "//components/web_modal",
     "//content/public/browser",
@@ -2795,6 +3277,7 @@
     "//gpu/command_buffer/client",
     "//media:media_buildflags",
     "//media/capture:capture_lib",
+    "//media/capture/video/chromeos/mojom:cros_camera_headers",
     "//mojo/public/c/system:headers",
     "//mojo/public/cpp/base",
     "//mojo/public/cpp/bindings",
@@ -2934,14 +3417,19 @@
     "//ash/webui/resources:shortcut_customization_app_resources",
     "//build:branding_buildflags",
     "//build/config/chromebox_for_meetings:buildflags",
+    "//cc/base",
     "//cc/paint",
     "//chrome/app:chromium_strings",
     "//chrome/app:command_ids",
     "//chrome/app:generated_resources",
+    "//chrome/app/resources:locale_settings",
     "//chrome/app/theme:chrome_unscaled_resources",
     "//chrome/app/theme:theme_resources",
     "//chrome/app/vector_icons",
+    "//chrome/browser:browser_themes",
     "//chrome/browser:resources",
+    "//chrome/browser:theme_properties",
+    "//chrome/browser/apps/platform_apps",
     "//chrome/browser/apps/platform_apps/api",
     "//chrome/browser/ash/mojo_service_manager",
     "//chrome/browser/ash/nearby:bluetooth_adapter_manager",
@@ -2960,6 +3448,7 @@
     "//chrome/browser/resources:component_extension_resources",
     "//chrome/browser/resources/chromeos:app_icon_resources",
     "//chrome/browser/resources/settings/chromeos:resources",
+    "//chrome/browser/supervised_user/supervised_user_features",
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings",
     "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
@@ -2969,7 +3458,7 @@
     "//chrome/common/apps/platform_apps/api",
     "//chrome/common/net",
     "//chromeos/ash/components/account_manager",
-    "//chromeos/ash/components/cryptohome",
+    "//chromeos/ash/components/assistant:buildflags",
     "//chromeos/ash/components/dbus",
     "//chromeos/ash/components/dbus:plugin_vm_service_proto",
     "//chromeos/ash/components/dbus:vm_disk_management_proto",
@@ -2978,11 +3467,11 @@
     "//chromeos/ash/components/dbus/arc",
     "//chromeos/ash/components/dbus/audio",
     "//chromeos/ash/components/dbus/biod",
+    "//chromeos/ash/components/dbus/biod:biod_proto",
     "//chromeos/ash/components/dbus/cdm_factory_daemon",
     "//chromeos/ash/components/dbus/cec_service",
     "//chromeos/ash/components/dbus/cros_healthd",
     "//chromeos/ash/components/dbus/cryptohome:attestation_proto",
-    "//chromeos/ash/components/dbus/cryptohome:cryptohome_proto",
     "//chromeos/ash/components/dbus/cups_proxy",
     "//chromeos/ash/components/dbus/dlcservice:dlcservice_proto",
     "//chromeos/ash/components/dbus/easy_unlock",
@@ -2996,7 +3485,6 @@
     "//chromeos/ash/components/dbus/kerberos",
     "//chromeos/ash/components/dbus/media_analytics",
     "//chromeos/ash/components/dbus/oobe_config",
-    "//chromeos/ash/components/dbus/os_install",
     "//chromeos/ash/components/dbus/patchpanel",
     "//chromeos/ash/components/dbus/pciguard",
     "//chromeos/ash/components/dbus/rgbkbd",
@@ -3012,8 +3500,7 @@
     "//chromeos/ash/components/device_activity",
     "//chromeos/ash/components/hibernate:buildflags",
     "//chromeos/ash/components/local_search_service/public/cpp",
-    "//chromeos/ash/components/login/auth/public:authpublic",
-    "//chromeos/ash/components/multidevice",
+    "//chromeos/ash/components/mojo_service_manager",
     "//chromeos/ash/components/multidevice:stub_multidevice_util",
     "//chromeos/ash/components/multidevice/logging",
     "//chromeos/ash/components/sync_wifi",
@@ -3022,9 +3509,9 @@
     "//chromeos/ash/services/cros_healthd/private/cpp",
     "//chromeos/components/cdm_factory_daemon:cdm_factory_daemon_browser",
     "//chromeos/components/cdm_factory_daemon/mojom",
+    "//chromeos/components/certificate_provider",
     "//chromeos/components/disks:prefs",
     "//chromeos/components/mojo_bootstrap",
-    "//chromeos/components/mojo_service_manager",
     "//chromeos/components/sensors",
     "//chromeos/constants",
     "//chromeos/crosapi/cpp",
@@ -3045,10 +3532,12 @@
     "//components/arc/common:arc_intent_helper_constants",
     "//components/component_updater",
     "//components/consent_auditor",
+    "//components/constrained_window",
     "//components/content_settings/core/common",
     "//components/country_codes",
     "//components/crash/core/app",
     "//components/crash/core/common",
+    "//components/crash/core/common:crash_key_lib",
     "//components/crx_file",
     "//components/device_event_log",
     "//components/download/public/common:public",
@@ -3068,6 +3557,9 @@
     "//components/metrics:serialization",
     "//components/metrics/structured:neutrino_logging",
     "//components/metrics/structured:neutrino_logging_util",
+    "//components/omnibox/browser:location_bar",
+    "//components/password_manager/core/browser",
+    "//components/password_manager/core/browser:hash_password_manager",
     "//components/permissions",
     "//components/policy:generated",
     "//components/proxy_config",
@@ -3075,7 +3567,6 @@
     "//components/reporting/proto:interface_proto",
     "//components/reporting/util:status",
     "//components/reporting/util:status_proto",
-    "//components/rlz",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/services/app_service/public/cpp:app_file_handling",
     "//components/services/app_service/public/cpp:types",
@@ -3086,26 +3577,30 @@
     "//components/session_manager:base",
     "//components/signin/public/base",
     "//components/spellcheck/browser",
+    "//components/startup_metric_utils/browser",
     "//components/strings:components_strings",
     "//components/sync/base",
     "//components/sync/chromeos",
     "//components/sync/engine",
     "//components/sync/protocol:util",
     "//components/sync_sessions",
+    "//components/tracing:startup_tracing",
     "//components/translate/core/browser",
-    "//components/ukm:ukm",
+    "//components/ukm",
     "//components/ukm:ukm_recorder",
+    "//components/unified_consent",
     "//components/user_prefs",
     "//components/variations",
     "//components/vector_icons",
-    "//components/version_info",
     "//components/version_info:channel",
     "//components/viz/host",
+    "//components/web_resource",
     "//components/webapps/browser",
     "//components/webapps/browser:constants",
     "//content/public/common",
     "//content/public/common:main_function_params",
     "//device/bluetooth/public/cpp",
+    "//extensions:extensions_browser_resources",
     "//extensions/browser/api",
     "//extensions/browser/api/feedback_private",
     "//extensions/browser/api/messaging",
@@ -3116,10 +3611,14 @@
     "//extensions/common:mojom",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/common",
+    "//gpu/command_buffer/service",
     "//gpu/config",
     "//gpu/ipc/common",
     "//gpu/ipc/common:memory_stats_sources",
-    "//media/capture/video/chromeos/mojom:cros_camera_shared",
+    "//ipc:message_support",
+    "//media",
+    "//media/capture:capture_switches",
+    "//media/capture/video/chromeos/mojom:cros_camera",
     "//media/capture/video/chromeos/public",
     "//mojo/public/cpp/bindings:struct_traits",
     "//mojo/public/mojom/base",
@@ -3129,11 +3628,13 @@
     "//remoting/host/chromeos:features",
     "//remoting/host/chromeos:remoting_service",
     "//rlz/buildflags",
+    "//sandbox/policy",
     "//services/audio/public/cpp",
     "//services/device/public/cpp/usb",
     "//services/metrics/public/cpp:gen_ukm_builders",
     "//services/network:network_service",
     "//services/preferences/public/cpp",
+    "//services/service_manager/public/cpp",
     "//services/tracing/public/mojom",
     "//third_party/blink/public/common:headers",
     "//third_party/libipp",
@@ -3142,6 +3643,7 @@
     "//third_party/re2",
     "//third_party/securemessage/proto",
     "//third_party/xdg_shared_mime_info",
+    "//third_party/zlib",
     "//third_party/zlib/google:compression_utils",
     "//third_party/zlib/google:zip",
     "//ui/accessibility:ax_enums_mojo",
@@ -3163,9 +3665,11 @@
     "//ui/events/blink",
     "//ui/file_manager:resources",
     "//ui/gfx:color_utils",
+    "//ui/gfx:gfx_switches",
     "//ui/gfx:memory_buffer",
     "//ui/gfx/codec",
     "//ui/gfx/geometry:geometry_skia",
+    "//ui/gl",
     "//ui/message_center",
     "//ui/native_theme",
     "//ui/ozone",
@@ -3179,6 +3683,10 @@
     deps += [ "//chromeos/ash/components/dbus/hiberman" ]
   }
 
+  if (enable_rlz) {
+    deps += [ "//components/rlz" ]
+  }
+
   if (is_cfm) {
     sources += [
       "chromebox_for_meetings/browser/cfm_browser_service.cc",
@@ -3292,6 +3800,11 @@
   sources = [ "attestation/attestation_key_payload.proto" ]
 }
 
+proto_library("device_configuration_proto") {
+  sources = [ "login/screens/recommend_apps/device_configuration.proto" ]
+  generate_python = false
+}
+
 proto_library("key_permissions_proto") {
   sources = [ "//third_party/cros_system_api/dbus/chaps/key_permissions.proto" ]
   generate_python = false
@@ -3299,6 +3812,20 @@
   proto_out_dir = "chrome/browser/ash/platform_keys/key_permissions"
 }
 
+proto_library("login_logout_event_proto") {
+  sources =
+      [ "../policy/messaging_layer/proto/synced/login_logout_event.proto" ]
+  deps = [
+    "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
+  ]
+}
+
+proto_library("lock_unlock_event_proto") {
+  sources = [ "../policy/messaging_layer/proto/synced/lock_unlock_event.proto" ]
+  deps = [
+    "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
+  ]
+}
 proto_library("print_job_info_proto") {
   sources = [ "printing/history/print_job_info.proto" ]
 }
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index a9dbdef..d42bf05 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -268,14 +268,14 @@
 #endif
 
 #if BUILDFLAG(ENABLE_RLZ)
-#include "components/rlz/rlz_tracker.h"
+#include "components/rlz/rlz_tracker.h"  // nogncheck
 #endif
 
 namespace ash {
 namespace {
 
-void ChromeOSVersionCallback(const std::string& version) {
-  base::SetLinuxDistro(std::string("CrOS ") + version);
+void ChromeOSVersionCallback(const absl::optional<std::string>& version) {
+  base::SetLinuxDistro("CrOS " + version.value_or("0.0.0.0"));
 }
 
 // Creates an instance of the NetworkPortalDetector implementation or a stub.
diff --git a/chrome/browser/ash/crosapi/browser_action.cc b/chrome/browser/ash/crosapi/browser_action.cc
index 83d3ae5..ff2c5366 100644
--- a/chrome/browser/ash/crosapi/browser_action.cc
+++ b/chrome/browser/ash/crosapi/browser_action.cc
@@ -87,6 +87,22 @@
   const bool should_trigger_session_restore_;
 };
 
+class LaunchAction final : public BrowserAction {
+ public:
+  LaunchAction() : BrowserAction(true) {}
+
+  void Perform(const VersionedBrowserService& service) override {
+    if (service.interface_version < mojom::BrowserService::kLaunchMinVersion) {
+      LOG(WARNING)
+          << "Lacros too old for Launch action - falling back to NewTab";
+      service.service->NewTab(/*should_trigger_session_restore=*/true,
+                              base::DoNothing());
+      return;
+    }
+    service.service->Launch(base::DoNothing());
+  }
+};
+
 namespace {
 crosapi::mojom::OpenUrlParams_SwitchToTabPathBehavior ConvertPathBehavior(
     NavigateParams::PathBehavior path_behavior) {
@@ -281,6 +297,11 @@
 }
 
 // static
+std::unique_ptr<BrowserAction> BrowserAction::Launch() {
+  return std::make_unique<LaunchAction>();
+}
+
+// static
 std::unique_ptr<BrowserAction> BrowserAction::NewWindowForDetachingTab(
     base::StringPiece16 tab_id_str,
     base::StringPiece16 group_id_str,
diff --git a/chrome/browser/ash/crosapi/browser_action.h b/chrome/browser/ash/crosapi/browser_action.h
index 270a1b1d..921e7e6 100644
--- a/chrome/browser/ash/crosapi/browser_action.h
+++ b/chrome/browser/ash/crosapi/browser_action.h
@@ -37,6 +37,7 @@
       bool should_trigger_session_restore);
   static std::unique_ptr<BrowserAction> NewTab(
       bool should_trigger_session_restore);
+  static std::unique_ptr<BrowserAction> Launch();
   static std::unique_ptr<BrowserAction> NewWindowForDetachingTab(
       base::StringPiece16 tab_id_str,
       base::StringPiece16 group_id_str,
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 97c9bf2..4186238a 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -462,6 +462,10 @@
   PerformOrEnqueue(BrowserAction::NewTab(should_trigger_session_restore));
 }
 
+void BrowserManager::Launch() {
+  PerformOrEnqueue(BrowserAction::Launch());
+}
+
 void BrowserManager::OpenUrl(
     const GURL& url,
     crosapi::mojom::OpenUrlFrom from,
diff --git a/chrome/browser/ash/crosapi/browser_manager.h b/chrome/browser/ash/crosapi/browser_manager.h
index 1034493..75eea10 100644
--- a/chrome/browser/ash/crosapi/browser_manager.h
+++ b/chrome/browser/ash/crosapi/browser_manager.h
@@ -173,6 +173,11 @@
   // window. See crosapi::mojom::BrowserService::NewTab for more details.
   void NewTab(bool should_trigger_session_restore);
 
+  // Similar to NewWindow and NewTab. If a suitable window exists, a new tab is
+  // added. Otherwise a new window is created with session restore (no new tab
+  // is added to that).
+  void Launch();
+
   // Opens the specified URL in lacros-chrome. If it is not running,
   // it launches lacros-chrome with the given URL.
   // See crosapi::mojom::BrowserService::OpenUrl for more details.
diff --git a/chrome/browser/ash/crosapi/parent_access_ash.cc b/chrome/browser/ash/crosapi/parent_access_ash.cc
index fd3e5a2c..45938fe 100644
--- a/chrome/browser/ash/crosapi/parent_access_ash.cc
+++ b/chrome/browser/ash/crosapi/parent_access_ash.cc
@@ -38,10 +38,14 @@
               parent_access_ui::mojom::WebApprovalsParams::New(
                   url, child_display_name, favicon_bitmap)));
 
-  // TODO(b/200587178): pass ParentAccessDialogCallback when it has been
-  // defined.
   chromeos::ParentAccessDialog::ShowError show_dialog_result =
-      chromeos::ParentAccessDialog::Show(std::move(params));
+      chromeos::ParentAccessDialog::Show(
+          std::move(params),
+          base::BindOnce(
+              [](std::unique_ptr<chromeos::ParentAccessDialog::Result> result)
+                  -> void {
+                // TODO(b/200587178): Handle ParentAccessDialogCallback.
+              }));
 
   crosapi::mojom::ParentAccessResultPtr result =
       crosapi::mojom::ParentAccessResult::New();
diff --git a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
index da81393..bfc0090 100644
--- a/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
+++ b/chrome/browser/ash/crosapi/test_mojo_connection_manager_unittest.cc
@@ -85,6 +85,7 @@
   void NewGuestWindow(NewGuestWindowCallback callback) override {}
   void NewTab(bool should_trigger_session_restore,
               NewTabCallback callback) override {}
+  void Launch(LaunchCallback callback) override {}
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
                OpenUrlCallback callback) override {}
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index d95d85a..92e9a7ca 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -53,7 +53,7 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/chrome_features.h"
@@ -473,8 +473,8 @@
       // open the file in the browser, too.
       // TODO(b/247038054) Add user preference to decide whether or not the
       // dialog should be shown.
-      return chromeos::cloud_upload::UploadAndOpen(
-          profile, file_urls, chromeos::cloud_upload::UploadType::kDrive,
+      return ash::cloud_upload::UploadAndOpen(
+          profile, file_urls, ash::cloud_upload::UploadType::kDrive,
           /*show_dialog=*/false);
     }
   } else {
@@ -563,8 +563,8 @@
       // TODO(b/247038054) Add user preference to decide whether or not the
       // dialog should be shown.
       LOG(ERROR) << "File can be moved to ODFS";
-      return chromeos::cloud_upload::UploadAndOpen(
-          profile, file_urls, chromeos::cloud_upload::UploadType::kOneDrive,
+      return ash::cloud_upload::UploadAndOpen(
+          profile, file_urls, ash::cloud_upload::UploadType::kOneDrive,
           /*show_dialog=*/false);
     }
   } else {
diff --git a/chrome/browser/ash/file_manager/trash_common_util.cc b/chrome/browser/ash/file_manager/trash_common_util.cc
index a953fb5b..d057168 100644
--- a/chrome/browser/ash/file_manager/trash_common_util.cc
+++ b/chrome/browser/ash/file_manager/trash_common_util.cc
@@ -16,6 +16,8 @@
 constexpr char kFilesFolderName[] = "files";
 constexpr char kTrashInfoExtension[] = ".trashinfo";
 constexpr char kTrackedDirectoryName[] = "user.TrackedDirectoryName";
+constexpr char kDirectorySetupHistogramName[] =
+    "FileBrowser.Trash.DirectorySetupFailed";
 
 TrashLocation::TrashLocation(const base::FilePath supplied_relative_folder_path,
                              const base::FilePath supplied_mount_point_path,
diff --git a/chrome/browser/ash/file_manager/trash_common_util.h b/chrome/browser/ash/file_manager/trash_common_util.h
index a9292a2..f018f1a5 100644
--- a/chrome/browser/ash/file_manager/trash_common_util.h
+++ b/chrome/browser/ash/file_manager/trash_common_util.h
@@ -30,6 +30,10 @@
 // to track the files and info directories for deletion by cryptohome.
 extern const char kTrackedDirectoryName[];
 
+// The histogram used to record the success or failure of the lazy creation of
+// the Trash directory and its children.
+extern const char kDirectorySetupHistogramName[];
+
 struct TrashLocation {
   TrashLocation(const base::FilePath supplied_relative_folder_path,
                 const base::FilePath supplied_mount_point_path,
@@ -92,6 +96,17 @@
     Profile* profile,
     const base::FilePath& base_path);
 
+// Enum of possible UMA values for histogram FileBrowser.Trash.DirectorySetup.
+// Keep the order of this in sync with FileManagerTrashDirectorySetupStep in
+// tools/metrics/histograms/enums.xml.
+enum class DirectorySetupUmaType {
+  FAILED_INFO_FOLDER = 0,
+  FAILED_FILES_FOLDER = 1,
+  FAILED_XATTR = 2,
+  FAILED_PARENT_FOLDER_PERMISSIONS = 3,
+  kMaxValue = FAILED_PARENT_FOLDER_PERMISSIONS,
+};
+
 }  // namespace file_manager::trash
 
 #endif  // CHROME_BROWSER_ASH_FILE_MANAGER_TRASH_COMMON_UTIL_H_
diff --git a/chrome/browser/ash/file_manager/trash_io_task.cc b/chrome/browser/ash/file_manager/trash_io_task.cc
index 9b7c067..47c36f9 100644
--- a/chrome/browser/ash/file_manager/trash_io_task.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task.cc
@@ -6,6 +6,7 @@
 
 #include <sys/xattr.h>
 
+#include "ash/metrics/histogram_macros.h"
 #include "base/callback.h"
 #include "base/containers/adapters.h"
 #include "base/files/file_util.h"
@@ -85,10 +86,15 @@
                            base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
 }
 
+void RecordDirectorySetupMetric(trash::DirectorySetupUmaType type) {
+  UMA_HISTOGRAM_ENUMERATION(trash::kDirectorySetupHistogramName, type);
+}
+
 base::File::Error SetTrackedExtendedAttribute(const base::FilePath& path) {
   auto tracked_name = base::StrCat({"trash_", path.BaseName().value()});
   if (lsetxattr(path.value().c_str(), trash::kTrackedDirectoryName,
                 tracked_name.c_str(), tracked_name.size(), 0) < 0) {
+    RecordDirectorySetupMetric(trash::DirectorySetupUmaType::FAILED_XATTR);
     PLOG(ERROR) << "Failed to set the xattr";
     return base::File::FILE_ERROR_FAILED;
   }
@@ -388,6 +394,13 @@
     const storage::FileSystemURL trash_subdirectory,
     base::File::Error error) {
   if (error != base::File::FILE_OK) {
+    auto failed_directory_uma_type =
+        (trash_subdirectory == it->second.trash_files)
+            ? trash::DirectorySetupUmaType::FAILED_FILES_FOLDER
+            : trash::DirectorySetupUmaType::FAILED_INFO_FOLDER;
+    RecordDirectorySetupMetric(failed_directory_uma_type);
+    LOG(ERROR) << "Failed setting up a trash subfolder: "
+               << static_cast<int>(failed_directory_uma_type);
     // TODO(b/231830211): We can potentially continue if one .Trash directory
     // fails to create, but we should also rollback if the files directory
     // succeeds but info fails.
@@ -419,6 +432,8 @@
     trash::TrashPathsMap::const_iterator& it,
     bool set_permissions_success) {
   if (!set_permissions_success) {
+    RecordDirectorySetupMetric(
+        trash::DirectorySetupUmaType::FAILED_PARENT_FOLDER_PERMISSIONS);
     LOG(ERROR) << "Failed setting directory permissions";
     Complete(State::kError);
     return;
diff --git a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
index b6a55c4..56f32e44 100644
--- a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/time/time_override.h"
 #include "chrome/browser/ash/crostini/crostini_manager.h"
@@ -198,6 +199,8 @@
   // path that adds the drive mount point is exercised.
   drive::DriveIntegrationServiceFactory::GetForProfile(profile_.get());
 
+  base::HistogramTester histogram_tester;
+
   std::string foo_contents = base::RandBytesAsString(kTestFileSize);
   const base::FilePath file_path = downloads_dir_.Append("foo.txt");
   ASSERT_TRUE(base::WriteFile(file_path, foo_contents));
@@ -224,9 +227,12 @@
   run_loop.Run();
 
   AssertTrashSetup(downloads_dir_);
+  histogram_tester.ExpectTotalCount(trash::kDirectorySetupHistogramName, 0);
 }
 
 TEST_F(TrashIOTaskTest, OrphanedFilesAreOverwritten) {
+  base::HistogramTester histogram_tester;
+
   std::string foo_contents = base::RandBytesAsString(kTestFileSize);
   std::string file_name("foo.txt");
   const base::FilePath file_path = downloads_dir_.Append(file_name);
@@ -278,9 +284,13 @@
   AssertTrashSetup(downloads_dir_);
   ExpectFileContents(GenerateInfoPath(file_name), file_trashinfo_contents);
   ExpectFileContents(GenerateFilesPath(file_name), foo_contents);
+
+  histogram_tester.ExpectTotalCount(trash::kDirectorySetupHistogramName, 0);
 }
 
 TEST_F(TrashIOTaskTest, MultipleFilesInvokeProgress) {
+  base::HistogramTester histogram_tester;
+
   std::string foo_contents = base::RandBytesAsString(kTestFileSize);
   std::string file_name_1("foo.txt");
   const base::FilePath file_path_1 = downloads_dir_.Append(file_name_1);
@@ -359,6 +369,8 @@
   ExpectFileContents(GenerateInfoPath(file_name_2), file_trashinfo_contents_2);
   ExpectFileContents(GenerateFilesPath(file_name_1), foo_contents);
   ExpectFileContents(GenerateFilesPath(file_name_2), foo_contents);
+
+  histogram_tester.ExpectTotalCount(trash::kDirectorySetupHistogramName, 0);
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/guest_os/guest_id.cc b/chrome/browser/ash/guest_os/guest_id.cc
index 37cba82..c0672ec9 100644
--- a/chrome/browser/ash/guest_os/guest_id.cc
+++ b/chrome/browser/ash/guest_os/guest_id.cc
@@ -149,9 +149,9 @@
   auto* pref_service = profile->GetPrefs();
   ScopedListPrefUpdate updater(pref_service, prefs::kGuestOsContainers);
   base::Value::List& update_list = updater.Get();
-  auto it = std::find_if(
-      update_list.begin(), update_list.end(),
-      [&](const auto& dict) { return MatchContainerDict(dict, container_id); });
+  auto it = base::ranges::find_if(update_list, [&](const auto& dict) {
+    return MatchContainerDict(dict, container_id);
+  });
   if (it != update_list.end())
     update_list.erase(it);
 }
@@ -160,9 +160,7 @@
   auto* pref_service = profile->GetPrefs();
   ScopedListPrefUpdate updater(pref_service, prefs::kGuestOsContainers);
   base::Value::List& update_list = updater.Get();
-  auto it = std::find_if(
-      update_list.begin(), update_list.end(),
-      [&](const auto& dict) { return VmTypeFromPref(dict) == vm_type; });
+  auto it = base::ranges::find(update_list, vm_type, &VmTypeFromPref);
   if (it != update_list.end())
     update_list.erase(it);
 }
@@ -184,9 +182,9 @@
                          const std::string& key,
                          base::Value value) {
   ScopedListPrefUpdate updater(profile->GetPrefs(), prefs::kGuestOsContainers);
-  auto it = std::find_if(
-      updater->begin(), updater->end(),
-      [&](const auto& dict) { return MatchContainerDict(dict, container_id); });
+  auto it = base::ranges::find_if(*updater, [&](const auto& dict) {
+    return MatchContainerDict(dict, container_id);
+  });
   if (it != updater->end()) {
     if (base::Contains(*kPropertiesAllowList, key)) {
       it->SetKey(key, std::move(value));
diff --git a/chrome/browser/ash/hats/hats_notification_controller.cc b/chrome/browser/ash/hats/hats_notification_controller.cc
index db05e82..8fa5372 100644
--- a/chrome/browser/ash/hats/hats_notification_controller.cc
+++ b/chrome/browser/ash/hats/hats_notification_controller.cc
@@ -340,9 +340,10 @@
   context[KeyEnumToString(DeviceInfoKey::BROWSER)] =
       version_info::GetVersionNumber();
 
+  absl::optional<std::string> version = chromeos::version_loader::GetVersion(
+      chromeos::version_loader::VERSION_FULL);
   context[KeyEnumToString(DeviceInfoKey::PLATFORM)] =
-      chromeos::version_loader::GetVersion(
-          chromeos::version_loader::VERSION_FULL);
+      version.value_or("0.0.0.0");
 
   context[KeyEnumToString(DeviceInfoKey::FIRMWARE)] =
       chromeos::version_loader::GetFirmware();
diff --git a/chrome/browser/ash/locale_change_guard_unittest.cc b/chrome/browser/ash/locale_change_guard_unittest.cc
index f33dee4..172bd86e 100644
--- a/chrome/browser/ash/locale_change_guard_unittest.cc
+++ b/chrome/browser/ash/locale_change_guard_unittest.cc
@@ -19,6 +19,7 @@
 // switched between different regions within the same language.
 const char* const kShowNotificationLanguages[] = {
     "af",   // Afrikaans
+    "ak",   // Twi
     "am",   // Amharic
     "an",   // Aragonese
     "ar",   // Arabic
@@ -39,6 +40,7 @@
     "cs",   // Czech
     "cy",   // Welsh
     "da",   // Danish
+    "ee",   // Ewe
     "el",   // Greek
     "eo",   // Esperanto
     "es",   // Spanish
@@ -75,10 +77,12 @@
     "kn",   // Kannada
     "ko",   // Korean
     "kok",  // Konkani
+    "kri",  // Krio
     "ku",   // Kurdish
     "ky",   // Kyrgyz
     "la",   // Latin
     "lb",   // Luxembourgish
+    "lg",   // Luganda
     "ln",   // Lingala
     "lo",   // Laothian
     "lt",   // Lithuanian
@@ -98,6 +102,7 @@
     "nl",   // Dutch
     "nn",   // Norwegian (Nynorsk)
     "no",   // Norwegian
+    "nso",  // Sepedi
     "ny",   // Nyanja
     "oc",   // Occitan
     "om",   // Oromo
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index bbe3e74..a8fbce98 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -882,8 +882,6 @@
 
 // Attestation enrollment.
 IN_PROC_BROWSER_TEST_F(AutoEnrollmentEmbeddedPolicyServer, Attestation) {
-  // Even though the server would allow device attributes update, Chrome OS will
-  // not attempt that for attestation enrollment.
   policy_server_.SetUpdateDeviceAttributesPermission(true);
 
   AllowlistSimpleChallengeSigningKey();
@@ -895,6 +893,9 @@
       test::kTestDomain));
 
   host()->StartWizard(AutoEnrollmentCheckScreenView::kScreenId);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
+  enrollment_ui_.SubmitDeviceAttributes(test::values::kAssetId,
+                                        test::values::kLocation);
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
   EXPECT_TRUE(InstallAttributes::Get()->IsCloudManaged());
diff --git a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
index bb98fe4e..c743ae0 100644
--- a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
+++ b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.cc
@@ -15,7 +15,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/login/enrollment/enrollment_uma.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_client_factory_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h"
 #include "chrome/browser/ash/policy/core/policy_oauth2_token_fetcher.h"
@@ -219,19 +218,6 @@
 }
 
 void EnterpriseEnrollmentHelperImpl::GetDeviceAttributeUpdatePermission() {
-  if (!auth_data_.has_oauth_token()) {
-    // Checking whether the device attributes can be updated requires knowning
-    // which user is performing enterprise enrollment, because the permission is
-    // tied to a user.
-    // For enterprise enrollment authorized by attestation or an enrollment
-    // token, the current user is unknown.
-    // A possible follow-up (tracked in https://crbug.com/942013) will be to
-    // allow the first affiliated user that signs in and has the permission to
-    // edit device attributes.
-    OnDeviceAttributeUpdatePermission(/*granted=*/false);
-    return;
-  }
-
   policy::BrowserPolicyConnectorAsh* connector =
       g_browser_process->platform_part()->browser_policy_connector_ash();
   // Don't update device attributes for Active Directory management.
@@ -243,25 +229,54 @@
       connector->GetDeviceCloudPolicyManager();
   policy::CloudPolicyClient* client = policy_manager->core()->client();
 
+  absl::optional<policy::DMAuth> auth =
+      GetDMAuthForDeviceAttributeUpdate(client);
+  if (!auth.has_value()) {
+    // There's no information about the enrolling user or device identity so
+    // device attributes update permission can't be fetched. Assume "no
+    // permission".
+    OnDeviceAttributeUpdatePermission(/*granted=*/false);
+    return;
+  }
   client->GetDeviceAttributeUpdatePermission(
-      auth_data_.Clone(),
+      std::move(auth.value()),
       base::BindOnce(
           &EnterpriseEnrollmentHelperImpl::OnDeviceAttributeUpdatePermission,
           weak_ptr_factory_.GetWeakPtr()));
 }
 
+absl::optional<policy::DMAuth>
+EnterpriseEnrollmentHelperImpl::GetDMAuthForDeviceAttributeUpdate(
+    policy::CloudPolicyClient* device_cloud_policy_client) {
+  // Checking whether the device attributes can be updated requires either
+  // knowing which user is performing enterprise enrollment, or which device
+  // is performing the attestation-based enrollment.
+  if (auth_data_.has_oauth_token()) {
+    return auth_data_.Clone();
+  } else if (enrollment_config_.is_mode_attestation()) {
+    return policy::DMAuth::FromDMToken(device_cloud_policy_client->dm_token());
+  } else {
+    return {};
+  }
+}
+
 void EnterpriseEnrollmentHelperImpl::UpdateDeviceAttributes(
     const std::string& asset_id,
     const std::string& location) {
-  DCHECK(!auth_data_.empty());
   policy::BrowserPolicyConnectorAsh* connector =
       g_browser_process->platform_part()->browser_policy_connector_ash();
   policy::DeviceCloudPolicyManagerAsh* policy_manager =
       connector->GetDeviceCloudPolicyManager();
   policy::CloudPolicyClient* client = policy_manager->core()->client();
 
+  absl::optional<policy::DMAuth> auth =
+      GetDMAuthForDeviceAttributeUpdate(client);
+
+  // If we got here, we must have successfully run GetDeviceAttributeUpdatePermission, which required a non-empty GetDMAuthForDeviceAttributeUpdate result.
+  DCHECK(auth.has_value());
+
   client->UpdateDeviceAttributes(
-      auth_data_.Clone(), asset_id, location,
+      std::move(auth.value()), asset_id, location,
       base::BindOnce(
           &EnterpriseEnrollmentHelperImpl::OnDeviceAttributeUploadCompleted,
           weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.h b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.h
index 27699d3..3146ac23 100644
--- a/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.h
+++ b/chrome/browser/ash/login/enrollment/enterprise_enrollment_helper_impl.h
@@ -12,7 +12,9 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/enrollment/enterprise_enrollment_helper.h"
+#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
+#include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/dm_auth.h"
 #include "components/policy/core/common/cloud/enterprise_metrics.h"
 #include "google_apis/gaia/google_service_auth_error.h"
@@ -81,6 +83,11 @@
   // `callback` is a callback, that was passed to ClearAuth() before.
   void OnSigninProfileCleared(base::OnceClosure callback);
 
+  // Returns either OAuth token or DM token needed for the device attribute
+  // update permission request.
+  absl::optional<policy::DMAuth> GetDMAuthForDeviceAttributeUpdate(
+      policy::CloudPolicyClient* device_cloud_policy_client);
+
   policy::EnrollmentConfig enrollment_config_;
   std::string enrolling_user_domain_;
   policy::LicenseType license_type_;
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 190e4f38..783cbd1 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -797,7 +797,7 @@
              failure.reason() == AuthFailure::MISSING_CRYPTOHOME) {
     ForceOnlineLoginForAccountId(last_login_attempt_account_id_);
     RecordReauthReason(last_login_attempt_account_id_,
-                       ReauthReason::MISSING_CRYPTOHOME);
+                       ReauthReason::kMissingCryptohome);
   } else if (is_known_user &&
              failure.reason() == AuthFailure::UNRECOVERABLE_CRYPTOHOME) {
     // TODO(chromium:1140868, dlunev): for now we route unrecoverable the same
@@ -806,7 +806,7 @@
     // chromium level, including making the decision user-driven.
     ForceOnlineLoginForAccountId(last_login_attempt_account_id_);
     RecordReauthReason(last_login_attempt_account_id_,
-                       ReauthReason::UNRECOVERABLE_CRYPTOHOME);
+                       ReauthReason::kUnrecoverableCryptohome);
   } else {
     // Check networking after trying to login in case user is
     // cached locally or the local admin account.
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc
index 9d692a9..38ffdca2 100644
--- a/chrome/browser/ash/login/password_change_browsertest.cc
+++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -245,7 +245,7 @@
   SetGaiaScreenCredentials(test_account_id_, "new user password");
   WaitForPasswordChangeScreen();
   histogram_tester.ExpectBucketCount("Login.PasswordChanged.ReauthReason",
-                                     ReauthReason::OTHER, 1);
+                                     ReauthReason::kOther, 1);
 
   test::OobeJS().CreateVisibilityWaiter(true, kPasswordStep)->Wait();
 
@@ -267,7 +267,7 @@
   SetUpStubAuthenticatorAndAttemptLogin("old user password");
   WaitForPasswordChangeScreen();
   histogram_tester.ExpectBucketCount("Login.PasswordChanged.ReauthReason",
-                                     ReauthReason::OTHER, 1);
+                                     ReauthReason::kOther, 1);
 
   test::OobeJS().CreateVisibilityWaiter(true, kPasswordStep)->Wait();
 
@@ -435,7 +435,7 @@
   SetUpStubAuthenticatorAndAttemptLogin("old user password");
   WaitForPasswordChangeScreen();
   histogram_tester.ExpectBucketCount("Login.PasswordChanged.ReauthReason",
-                                     ReauthReason::INVALID_TOKEN_HANDLE, 1);
+                                     ReauthReason::kInvalidTokenHandle, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeTokenCheck, LoginScreenNoPasswordChange) {
@@ -449,7 +449,7 @@
   login_mixin_.LoginWithDefaultContext(login_mixin_.users().back());
   login_mixin_.WaitForActiveSession();
   histogram_tester.ExpectBucketCount("Login.PasswordNotChanged.ReauthReason",
-                                     ReauthReason::INVALID_TOKEN_HANDLE, 1);
+                                     ReauthReason::kInvalidTokenHandle, 1);
 }
 
 // Helper class to create NotificationDisplayServiceTester before notification
@@ -522,7 +522,7 @@
   SetUpStubAuthenticatorAndAttemptLogin("old user password");
   WaitForPasswordChangeScreen();
   histogram_tester.ExpectBucketCount("Login.PasswordChanged.ReauthReason",
-                                     ReauthReason::INVALID_TOKEN_HANDLE, 1);
+                                     ReauthReason::kInvalidTokenHandle, 1);
 }
 
 // Notification should not be triggered because token was checked on the login
diff --git a/chrome/browser/ash/login/reauth_stats.cc b/chrome/browser/ash/login/reauth_stats.cc
index 7288491..4b214c3 100644
--- a/chrome/browser/ash/login/reauth_stats.cc
+++ b/chrome/browser/ash/login/reauth_stats.cc
@@ -11,33 +11,44 @@
 
 namespace ash {
 
+namespace {
+
+ReauthReason GetReauthReason(const user_manager::KnownUser& known_user,
+                             const AccountId& account_id) {
+  return static_cast<ReauthReason>(
+      known_user.FindReauthReason(account_id)
+          .value_or(static_cast<int>(ReauthReason::kNone)));
+}
+
+}  // namespace
+
 void RecordReauthReason(const AccountId& account_id, ReauthReason reason) {
-  if (reason == ReauthReason::NONE)
+  if (reason == ReauthReason::kNone)
     return;
   user_manager::KnownUser known_user(g_browser_process->local_state());
-  if (known_user.FindReauthReason(account_id).value_or(ReauthReason::NONE) ==
-      reason) {
+  if (GetReauthReason(known_user, account_id) == reason)
     return;
-  }
-  LOG(WARNING) << "Reauth reason updated: " << reason;
+
+  LOG(WARNING) << "Reauth reason updated: " << static_cast<int>(reason);
   known_user.UpdateReauthReason(account_id, static_cast<int>(reason));
 }
 
 void SendReauthReason(const AccountId& account_id, bool password_changed) {
   user_manager::KnownUser known_user(g_browser_process->local_state());
-  ReauthReason reauth_reason = static_cast<ReauthReason>(
-      known_user.FindReauthReason(account_id).value_or(ReauthReason::NONE));
-  if (reauth_reason == ReauthReason::NONE)
+  ReauthReason reauth_reason = GetReauthReason(known_user, account_id);
+  if (reauth_reason == ReauthReason::kNone)
     return;
   if (password_changed) {
     base::UmaHistogramEnumeration("Login.PasswordChanged.ReauthReason",
-                                  reauth_reason, NUM_REAUTH_FLOW_REASONS);
+                                  reauth_reason,
+                                  ReauthReason::kNumReauthFlowReasons);
   } else {
     base::UmaHistogramEnumeration("Login.PasswordNotChanged.ReauthReason",
-                                  reauth_reason, NUM_REAUTH_FLOW_REASONS);
+                                  reauth_reason,
+                                  ReauthReason::kNumReauthFlowReasons);
   }
   known_user.UpdateReauthReason(account_id,
-                                static_cast<int>(ReauthReason::NONE));
+                                static_cast<int>(ReauthReason::kNone));
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/gaia_password_changed_screen.cc b/chrome/browser/ash/login/screens/gaia_password_changed_screen.cc
index a47671e..264a295 100644
--- a/chrome/browser/ash/login/screens/gaia_password_changed_screen.cc
+++ b/chrome/browser/ash/login/screens/gaia_password_changed_screen.cc
@@ -85,7 +85,7 @@
 
 void GaiaPasswordChangedScreen::CancelPasswordChangedFlow() {
   if (account_id_.is_valid()) {
-    RecordReauthReason(account_id_, ReauthReason::PASSWORD_UPDATE_SKIPPED);
+    RecordReauthReason(account_id_, ReauthReason::kPasswordUpdateSkipped);
   }
   SigninProfileHandler::Get()->ClearSigninProfile(
       base::BindOnce(&GaiaPasswordChangedScreen::OnCookiesCleared,
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.cc b/chrome/browser/ash/login/screens/user_selection_screen.cc
index 6f16bad..f5fa463 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen.cc
@@ -466,7 +466,7 @@
   // this might be a leftover from an old version.
   if (has_gaia_account &&
       token_status == user_manager::User::OAUTH2_TOKEN_STATUS_INVALID)
-    RecordReauthReason(user->GetAccountId(), ReauthReason::OTHER);
+    RecordReauthReason(user->GetAccountId(), ReauthReason::kOther);
 
   // We need to force an online signin if the user is marked as requiring it or
   // if there's an invalid OAUTH token that needs to be refreshed.
@@ -693,7 +693,7 @@
     const AccountId& account_id,
     TokenHandleUtil::TokenHandleStatus status) {
   if (status == TokenHandleUtil::INVALID) {
-    RecordReauthReason(account_id, ReauthReason::INVALID_TOKEN_HANDLE);
+    RecordReauthReason(account_id, ReauthReason::kInvalidTokenHandle);
     SetAuthType(account_id, proximity_auth::mojom::AuthType::ONLINE_SIGN_IN,
                 std::u16string());
   }
@@ -821,7 +821,7 @@
 
 void UserSelectionScreen::OnInvalidSyncToken(const AccountId& account_id) {
   RecordReauthReason(account_id,
-                     ReauthReason::SAML_PASSWORD_SYNC_TOKEN_VALIDATION_FAILED);
+                     ReauthReason::kSamlPasswordSyncTokenValidationFailed);
   SetAuthType(account_id, proximity_auth::mojom::AuthType::ONLINE_SIGN_IN,
               std::u16string());
 }
diff --git a/chrome/browser/ash/login/session/user_session_initializer.cc b/chrome/browser/ash/login/session/user_session_initializer.cc
index e7cf7f0..0bf6b30 100644
--- a/chrome/browser/ash/login/session/user_session_initializer.cc
+++ b/chrome/browser/ash/login/session/user_session_initializer.cc
@@ -55,7 +55,7 @@
 
 #if BUILDFLAG(ENABLE_RLZ)
 #include "chrome/browser/rlz/chrome_rlz_tracker_delegate.h"
-#include "components/rlz/rlz_tracker.h"
+#include "components/rlz/rlz_tracker.h"  // nogncheck
 #endif
 
 namespace ash {
diff --git a/chrome/browser/ash/login/signin/auth_error_observer.cc b/chrome/browser/ash/login/signin/auth_error_observer.cc
index f1f62af..fe394a0 100644
--- a/chrome/browser/ash/login/signin/auth_error_observer.cc
+++ b/chrome/browser/ash/login/signin/auth_error_observer.cc
@@ -91,7 +91,7 @@
 
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
         account_id, user_manager::User::OAUTH2_TOKEN_STATUS_INVALID);
-    RecordReauthReason(account_id, ReauthReason::SYNC_FAILED);
+    RecordReauthReason(account_id, ReauthReason::kSyncFailed);
   } else if (auth_error.state() == GoogleServiceAuthError::NONE) {
     if (user->oauth_token_status() ==
         user_manager::User::OAUTH2_TOKEN_STATUS_INVALID) {
diff --git a/chrome/browser/ash/login/signin/offline_signin_limiter.cc b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
index 5d5311f..72258f9 100644
--- a/chrome/browser/ash/login/signin/offline_signin_limiter.cc
+++ b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
@@ -368,9 +368,9 @@
   user_manager::UserManager::Get()->SaveForceOnlineSignin(user->GetAccountId(),
                                                           true);
   if (user->using_saml())
-    RecordReauthReason(user->GetAccountId(), ReauthReason::SAML_REAUTH_POLICY);
+    RecordReauthReason(user->GetAccountId(), ReauthReason::kSamlReauthPolicy);
   else
-    RecordReauthReason(user->GetAccountId(), ReauthReason::GAIA_REAUTH_POLICY);
+    RecordReauthReason(user->GetAccountId(), ReauthReason::kGaiaReauthPolicy);
   offline_signin_limit_timer_->Stop();
 }
 
@@ -393,10 +393,10 @@
 
   if (user->using_saml()) {
     RecordReauthReason(user->GetAccountId(),
-                       ReauthReason::SAML_LOCK_SCREEN_REAUTH_POLICY);
+                       ReauthReason::kSamlLockScreenReauthPolicy);
   } else {
     RecordReauthReason(user->GetAccountId(),
-                       ReauthReason::GAIA_LOCK_SCREEN_REAUTH_POLICY);
+                       ReauthReason::kGaiaLockScreenReauthPolicy);
   }
   offline_lock_screen_signin_limit_timer_->Stop();
 }
diff --git a/chrome/browser/ash/login/signin/signin_error_notifier.cc b/chrome/browser/ash/login/signin/signin_error_notifier.cc
index 961f21d9..cdb67e50 100644
--- a/chrome/browser/ash/login/signin/signin_error_notifier.cc
+++ b/chrome/browser/ash/login/signin/signin_error_notifier.cc
@@ -197,7 +197,7 @@
     TokenHandleUtil::TokenHandleStatus status) {
   if (status != TokenHandleUtil::INVALID)
     return;
-  RecordReauthReason(account_id, ReauthReason::INVALID_TOKEN_HANDLE);
+  RecordReauthReason(account_id, ReauthReason::kInvalidTokenHandle);
   HandleDeviceAccountError(/*error_message=*/l10n_util::GetStringUTF16(
       IDS_SYNC_TOKEN_HANDLE_ERROR_BUBBLE_VIEW_MESSAGE));
 }
@@ -248,7 +248,7 @@
   if (!IsAccountManagerAvailable(profile_)) {
     // If this flag is disabled, Chrome OS does not have a concept of Secondary
     // Accounts. Preserve existing behavior.
-    RecordReauthReason(account_id, ReauthReason::SYNC_FAILED);
+    RecordReauthReason(account_id, ReauthReason::kSyncFailed);
     HandleDeviceAccountError(
         /*error_message=*/GetMessageBodyForDeviceAccountErrors(
             /*error=*/error_controller_->auth_error().state()));
@@ -259,7 +259,7 @@
   const CoreAccountId primary_account_id =
       identity_manager_->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
   if (error_account_id == primary_account_id) {
-    RecordReauthReason(account_id, ReauthReason::SYNC_FAILED);
+    RecordReauthReason(account_id, ReauthReason::kSyncFailed);
     HandleDeviceAccountError(
         /*error_message=*/GetMessageBodyForDeviceAccountErrors(
             /*error=*/error_controller_->auth_error().state()));
diff --git a/chrome/browser/ash/login/version_info_updater.cc b/chrome/browser/ash/login/version_info_updater.cc
index 0226cab..a3c2c43 100644
--- a/chrome/browser/ash/login/version_info_updater.cc
+++ b/chrome/browser/ash/login/version_info_updater.cc
@@ -123,14 +123,15 @@
 }
 
 void VersionInfoUpdater::UpdateVersionLabel() {
-  if (version_text_.empty())
+  if (!version_text_.has_value())
     return;
 
   std::string label_text = l10n_util::GetStringFUTF8(
       IDS_LOGIN_VERSION_LABEL_FORMAT,
       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
       base::UTF8ToUTF16(version_info::GetVersionNumber()),
-      base::UTF8ToUTF16(version_text_), base::UTF8ToUTF16(GetDeviceIdsLabel()));
+      base::UTF8ToUTF16(version_text_.value()),
+      base::UTF8ToUTF16(GetDeviceIdsLabel()));
 
   if (delegate_)
     delegate_->OnOSVersionLabelTextUpdated(label_text);
@@ -185,7 +186,7 @@
 
   return device_ids_text;
 }
-void VersionInfoUpdater::OnVersion(const std::string& version) {
+void VersionInfoUpdater::OnVersion(const absl::optional<std::string>& version) {
   version_text_ = version;
   UpdateVersionLabel();
 }
diff --git a/chrome/browser/ash/login/version_info_updater.h b/chrome/browser/ash/login/version_info_updater.h
index 8b97612b..745040c 100644
--- a/chrome/browser/ash/login/version_info_updater.h
+++ b/chrome/browser/ash/login/version_info_updater.h
@@ -80,7 +80,7 @@
   std::string GetDeviceIdsLabel();
 
   // Callback from VersionLoader giving the version.
-  void OnVersion(const std::string& version);
+  void OnVersion(const absl::optional<std::string>& version);
 
   // Callback from device::BluetoothAdapterFactory::GetAdapter.
   void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
@@ -91,7 +91,7 @@
       bool enabled);
 
   // Text obtained from OnVersion.
-  std::string version_text_;
+  absl::optional<std::string> version_text_;
 
   std::vector<base::CallbackListSubscription> subscriptions_;
 
diff --git a/chrome/browser/ash/mojo_service_manager/BUILD.gn b/chrome/browser/ash/mojo_service_manager/BUILD.gn
index 3141239..0d88af8d 100644
--- a/chrome/browser/ash/mojo_service_manager/BUILD.gn
+++ b/chrome/browser/ash/mojo_service_manager/BUILD.gn
@@ -15,7 +15,7 @@
   deps = [
     "//base",
     "//chromeos:features",
-    "//chromeos/components/mojo_service_manager",
+    "//chromeos/ash/components/mojo_service_manager",
     "//mojo/public/cpp/bindings",
   ]
 }
diff --git a/chrome/browser/ash/mojo_service_manager/OWNERS b/chrome/browser/ash/mojo_service_manager/OWNERS
index d350deb..31db841 100644
--- a/chrome/browser/ash/mojo_service_manager/OWNERS
+++ b/chrome/browser/ash/mojo_service_manager/OWNERS
@@ -1 +1 @@
-file://chromeos/components/mojo_service_manager/OWNERS
+file://chromeos/ash/components/mojo_service_manager/OWNERS
diff --git a/chrome/browser/ash/mojo_service_manager/connection_helper.cc b/chrome/browser/ash/mojo_service_manager/connection_helper.cc
index 45f2c07..4b0bbb82 100644
--- a/chrome/browser/ash/mojo_service_manager/connection_helper.cc
+++ b/chrome/browser/ash/mojo_service_manager/connection_helper.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
-#include "chromeos/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "chromeos/features.h"
 
 #if !BUILDFLAG(USE_REAL_CHROMEOS_SERVICES)
 #include "base/system/sys_info.h"
-#include "chromeos/components/mojo_service_manager/fake_mojo_service_manager.h"
+#include "chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h"
 #endif
 
 namespace {
diff --git a/chrome/browser/ash/net/network_diagnostics/network_diagnostics.cc b/chrome/browser/ash/net/network_diagnostics/network_diagnostics.cc
index 2573083..016d7b8 100644
--- a/chrome/browser/ash/net/network_diagnostics/network_diagnostics.cc
+++ b/chrome/browser/ash/net/network_diagnostics/network_diagnostics.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/ash/net/network_diagnostics/signal_strength_routine.h"
 #include "chrome/browser/ash/net/network_diagnostics/video_conferencing_routine.h"
 #include "chromeos/ash/components/dbus/debug_daemon/debug_daemon_client.h"
-#include "chromeos/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "components/device_event_log/device_event_log.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/cros_system_api/mojo/service_constants.h"
diff --git a/chrome/browser/ash/net/network_diagnostics/network_diagnostics.h b/chrome/browser/ash/net/network_diagnostics/network_diagnostics.h
index 9d9b4911..1053fdbb 100644
--- a/chrome/browser/ash/net/network_diagnostics/network_diagnostics.h
+++ b/chrome/browser/ash/net/network_diagnostics/network_diagnostics.h
@@ -10,7 +10,7 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/net/network_diagnostics/network_diagnostics_routine.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
diff --git a/chrome/browser/ash/net/network_health/network_health.cc b/chrome/browser/ash/net/network_health/network_health.cc
index 3d35a27..ebefbc14e 100644
--- a/chrome/browser/ash/net/network_health/network_health.cc
+++ b/chrome/browser/ash/net/network_health/network_health.cc
@@ -12,8 +12,8 @@
 
 #include "base/time/time.h"
 #include "chrome/browser/ash/net/network_health/network_health_constants.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "chromeos/ash/components/network/network_event_log.h"
-#include "chromeos/components/mojo_service_manager/connection.h"
 #include "chromeos/services/network_config/in_process_instance.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
diff --git a/chrome/browser/ash/net/network_health/network_health.h b/chrome/browser/ash/net/network_health/network_health.h
index bf7e711..880427e 100644
--- a/chrome/browser/ash/net/network_health/network_health.h
+++ b/chrome/browser/ash/net/network_health/network_health.h
@@ -11,7 +11,7 @@
 
 #include "base/timer/timer.h"
 #include "chrome/browser/ash/net/network_health/signal_strength_tracker.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_observer.h"
 #include "chromeos/services/network_health/public/mojom/network_health.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/chrome/browser/ash/net/network_portal_detector_impl_browsertest.cc b/chrome/browser/ash/net/network_portal_detector_impl_browsertest.cc
index b4cabca..92a54a0 100644
--- a/chrome/browser/ash/net/network_portal_detector_impl_browsertest.cc
+++ b/chrome/browser/ash/net/network_portal_detector_impl_browsertest.cc
@@ -68,6 +68,13 @@
   base::RunLoop().RunUntilIdle();
 }
 
+void SetDisconnected(const std::string& service_path) {
+  ShillServiceClient::Get()->Disconnect(dbus::ObjectPath(service_path),
+                                        base::DoNothing(),
+                                        base::BindOnce(&ErrorCallbackFunction));
+  base::RunLoop().RunUntilIdle();
+}
+
 void SetShillProxyAuthRequired() {
   ShillServiceClient::Get()->SetProperty(
       dbus::ObjectPath(kWifiServicePath),
@@ -140,20 +147,14 @@
     ASSERT_TRUE(default_network);
     EXPECT_EQ(default_network->GetPortalState(), portal_state);
     EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
-    EXPECT_EQ(display_service_->GetNotification(kNotificationId)->title(),
-              expected_title);
-    EXPECT_EQ(display_service_->GetNotification(kNotificationId)->message(),
-              expected_message);
+    EXPECT_EQ(GetNotificationTitle(), expected_title);
+    EXPECT_EQ(GetNotificationMessage(), expected_message);
     if (expected_button_title.empty()) {
       EXPECT_EQ(
           display_service_->GetNotification(kNotificationId)->buttons().size(),
           0);
     } else {
-      EXPECT_EQ(display_service_->GetNotification(kNotificationId)
-                    ->buttons()
-                    .front()
-                    .title,
-                expected_button_title);
+      EXPECT_EQ(GetNotificationButtonTitle(), expected_button_title);
     }
     EXPECT_EQ(portal_detector_status,
               network_portal_detector::GetInstance()->GetCaptivePortalStatus());
@@ -195,6 +196,21 @@
     network_portal_notification_controller_->SetIgnoreNoNetworkForTesting();
   }
 
+  const std::u16string GetNotificationTitle() {
+    return display_service_->GetNotification(kNotificationId)->title();
+  }
+
+  const std::u16string GetNotificationMessage() {
+    return display_service_->GetNotification(kNotificationId)->message();
+  }
+
+  const std::u16string GetNotificationButtonTitle() {
+    return display_service_->GetNotification(kNotificationId)
+        ->buttons()
+        .front()
+        .title;
+  }
+
  protected:
   AccountId test_account_id_;
   std::unique_ptr<NotificationDisplayServiceTester> display_service_;
@@ -342,4 +358,92 @@
       NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE);
 }
 
+IN_PROC_BROWSER_TEST_F(NetworkPortalDetectorImplBrowserTestUI2022Update,
+                       PortalStateChangedBetweenPortalStates) {
+  LoginUser(test_account_id_);
+  content::RunAllPendingInMessageLoop();
+
+  // User connects to portalled wifi.
+  SetConnected(kWifiServicePath);
+  SetState(shill::kStateRedirectFound);
+
+  // Verify notification properties.
+  chromeos::NetworkStateHandler* network_state_handler =
+      chromeos::NetworkHandler::Get()->network_state_handler();
+  const chromeos::NetworkState* default_network =
+      network_state_handler->DefaultNetwork();
+  ASSERT_TRUE(default_network);
+  EXPECT_EQ(default_network->GetPortalState(),
+            chromeos::NetworkState::PortalState::kPortal);
+  EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
+  EXPECT_EQ(GetNotificationTitle(),
+            l10n_util::GetStringUTF16(
+                IDS_NEW_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI));
+  EXPECT_EQ(GetNotificationMessage(),
+            l10n_util::GetStringFUTF16(
+                IDS_NEW_PORTAL_DETECTION_NOTIFICATION_MESSAGE, u"wifi"));
+  EXPECT_EQ(
+      GetNotificationButtonTitle(),
+      l10n_util::GetStringUTF16(IDS_NEW_PORTAL_DETECTION_NOTIFICATION_BUTTON));
+
+  // State changes to portal-suspected and check if notification properties
+  // change.
+  SetState(shill::kStatePortalSuspected);
+  ASSERT_TRUE(default_network);
+  EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
+  EXPECT_EQ(GetNotificationTitle(),
+            l10n_util::GetStringUTF16(
+                IDS_NEW_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI));
+  EXPECT_EQ(
+      GetNotificationMessage(),
+      l10n_util::GetStringFUTF16(
+          IDS_NEW_PORTAL_SUSPECTED_DETECTION_NOTIFICATION_MESSAGE, u"wifi"));
+  EXPECT_EQ(GetNotificationButtonTitle(),
+            l10n_util::GetStringUTF16(
+                IDS_NEW_PORTAL_SUSPECTED_DETECTION_NOTIFICATION_BUTTON));
+
+  // Explicitly close the notification.
+  display_service_->RemoveNotification(NotificationHandler::Type::TRANSIENT,
+                                       kNotificationId, /*by_user=*/true);
+}
+
+IN_PROC_BROWSER_TEST_F(NetworkPortalDetectorImplBrowserTestUI2022Update,
+                       ReconnectionNewNotification) {
+  LoginUser(test_account_id_);
+  content::RunAllPendingInMessageLoop();
+
+  // User connects to portalled wifi.
+  SetConnected(kWifiServicePath);
+  SetState(shill::kStateRedirectFound);
+  EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
+
+  // Disconnect from portalled wifi.
+  SetDisconnected(kWifiServicePath);
+  EXPECT_FALSE(display_service_->GetNotification(kNotificationId));
+
+  // Verify notification when reconnecting to same portalled wifi.
+  SetConnected(kWifiServicePath);
+  SetState(shill::kStateRedirectFound);
+  EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
+}
+
+IN_PROC_BROWSER_TEST_F(NetworkPortalDetectorImplBrowserTestUI2022Update,
+                       UserDismissedNotificationNoNewNotification) {
+  LoginUser(test_account_id_);
+  content::RunAllPendingInMessageLoop();
+
+  // User connects to portalled wifi.
+  SetConnected(kWifiServicePath);
+  SetState(shill::kStateRedirectFound);
+  EXPECT_TRUE(display_service_->GetNotification(kNotificationId));
+
+  // Close Notification.
+  display_service_->RemoveNotification(NotificationHandler::Type::TRANSIENT,
+                                       kNotificationId, /*by_user=*/true);
+  // Change state to redirect-found.
+  SetState(shill::kStateRedirectFound);
+  // Verify notification does not exist after user closed.
+  EXPECT_FALSE(display_service_->GetNotification(kNotificationId));
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.cc b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
index 0aa8bf79..ef5395c 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
@@ -426,8 +426,12 @@
   return false;
 }
 
-void ChildStatusCollector::OnOSVersion(const std::string& version) {
-  os_version_ = version;
+// TODO(https://crbug.com/1364425)
+// Make this function fallible when the optional passed in evaluated to
+// nullptr, instead of returning a dummy string.
+void ChildStatusCollector::OnOSVersion(
+    const absl::optional<std::string>& version) {
+  os_version_ = version.value_or("0.0.0.0");
 }
 
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.h b/chrome/browser/ash/policy/status_collector/child_status_collector.h
index b2e3c8f8..1850896b 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector.h
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector.h
@@ -90,7 +90,7 @@
 
  private:
   // Callbacks from chromeos::VersionLoader.
-  void OnOSVersion(const std::string& version);
+  void OnOSVersion(const absl::optional<std::string>& version);
 
   // Fetches all child data that is necessary to fill ChildStatusReportRequest.
   void FillChildStatusReportRequest(
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index a040feb..c14ae38 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -2990,8 +2990,11 @@
   return report_app_info_;
 }
 
-void DeviceStatusCollector::OnOSVersion(const std::string& version) {
-  os_version_ = version;
+// TODO(https://crbug.com/1364428)
+// Make this function fallible when the optional received is empty
+void DeviceStatusCollector::OnOSVersion(
+    const absl::optional<std::string>& version) {
+  os_version_ = version.value_or("0.0.0.0");
 }
 
 void DeviceStatusCollector::OnOSFirmware(
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.h b/chrome/browser/ash/policy/status_collector/device_status_collector.h
index ed26b7c..47cd02d 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.h
@@ -229,7 +229,7 @@
   void ClearCachedMemoryUsage();
 
   // Callbacks from chromeos::VersionLoader.
-  void OnOSVersion(const std::string& version);
+  void OnOSVersion(const absl::optional<std::string>& version);
   void OnOSFirmware(std::pair<const std::string&, const std::string&> version);
 
   // Callbacks from `chromeos::TpmManagerClient`.
diff --git a/chrome/browser/ash/privacy_hub/privacy_hub_util.cc b/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
index f8c620c..143d2cff 100644
--- a/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
+++ b/chrome/browser/ash/privacy_hub/privacy_hub_util.cc
@@ -9,6 +9,7 @@
 #include "ash/system/privacy_hub/microphone_privacy_switch_controller.h"
 #include "ash/system/privacy_hub/privacy_hub_controller.h"
 #include "chromeos/ash/components/audio/cras_audio_handler.h"
+#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
 
 namespace ash::privacy_hub_util {
 
diff --git a/chrome/browser/ash/privacy_hub/privacy_hub_util.h b/chrome/browser/ash/privacy_hub/privacy_hub_util.h
index 98eaa923..e8b7e8c 100644
--- a/chrome/browser/ash/privacy_hub/privacy_hub_util.h
+++ b/chrome/browser/ash/privacy_hub/privacy_hub_util.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_ASH_PRIVACY_HUB_PRIVACY_HUB_UTIL_H_
 
 #include "ash/public/cpp/privacy_hub_delegate.h"
-#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom.h"
+#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-forward.h"
 
 namespace ash {
 
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index e2e8b05..f35892a 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -1082,11 +1082,7 @@
   EXPECT_EQ(manager->state(), AppBannerManager::State::INACTIVE);
 }
 
-enum class ServiceWorkerCriteriaType {
-  kDisabled,
-  kSkipForInstalls,
-  kSkipAll,
-};
+enum class ServiceWorkerCriteriaType { kDisabled, kSkipForInstalls };
 
 class AppBannerServiceWorkerCriteriaTest
     : public AppBannerManagerBrowserTest,
@@ -1095,19 +1091,12 @@
   AppBannerServiceWorkerCriteriaTest() {
     switch (GetParam()) {
       case ServiceWorkerCriteriaType::kDisabled:
-        scoped_feature_list_.InitWithFeatures(
-            {}, {features::kSkipServiceWorkerCheckAll,
-                 features::kSkipServiceWorkerCheckInstallOnly});
+        scoped_feature_list_.InitAndDisableFeature(
+            features::kSkipServiceWorkerCheckInstallOnly);
         break;
       case ServiceWorkerCriteriaType::kSkipForInstalls:
-        scoped_feature_list_.InitWithFeatures(
-            {features::kSkipServiceWorkerCheckInstallOnly},
-            {features::kSkipServiceWorkerCheckAll});
-        break;
-      case ServiceWorkerCriteriaType::kSkipAll:
-        scoped_feature_list_.InitWithFeatures(
-            {features::kSkipServiceWorkerCheckAll},
-            {features::kSkipServiceWorkerCheckInstallOnly});
+        scoped_feature_list_.InitAndEnableFeature(
+            features::kSkipServiceWorkerCheckInstallOnly);
         break;
     }
   }
@@ -1150,9 +1139,6 @@
     case ServiceWorkerCriteriaType::kSkipForInstalls:
       expected_code = SERVICE_WORKER_NOT_REQUIRED;
       break;
-    case ServiceWorkerCriteriaType::kSkipAll:
-      expected_code = absl::nullopt;
-      break;
   }
 
   RunBannerTest(browser(), manager.get(),
@@ -1179,9 +1165,6 @@
     case ServiceWorkerCriteriaType::kSkipForInstalls:
       expected_code = SERVICE_WORKER_NOT_REQUIRED;
       break;
-    case ServiceWorkerCriteriaType::kSkipAll:
-      expected_code = absl::nullopt;
-      break;
   }
 
   RunBannerTest(browser(), manager.get(),
@@ -1200,8 +1183,7 @@
     All,
     AppBannerServiceWorkerCriteriaTest,
     testing::Values(ServiceWorkerCriteriaType::kDisabled,
-                    ServiceWorkerCriteriaType::kSkipForInstalls,
-                    ServiceWorkerCriteriaType::kSkipAll));
+                    ServiceWorkerCriteriaType::kSkipForInstalls));
 
 }  // namespace
 }  // namespace webapps
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 5031c52f..d4884f7 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -26,6 +26,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/autocomplete/zero_suggest_cache_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/autofill/strike_database_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -1145,6 +1146,12 @@
       prefs->SetString(omnibox::kZeroSuggestCachedResults, std::string());
       prefs->SetDict(omnibox::kZeroSuggestCachedResultsWithURL,
                      base::Value::Dict());
+
+      auto* zero_suggest_cache_service =
+          ZeroSuggestCacheServiceFactory::GetForProfile(profile_);
+      if (zero_suggest_cache_service) {
+        zero_suggest_cache_service->ClearCache();
+      }
     }
 
     // |search_prefetch_service| is null if |profile_| is off the record.
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 2002b44..53466794 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -34,6 +34,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/autocomplete/zero_suggest_cache_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/autofill/strike_database_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -109,6 +110,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/language/core/browser/url_language_histogram.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
+#include "components/omnibox/browser/zero_suggest_cache_service.h"
 #include "components/origin_trials/browser/prefservice_persistence_provider.h"
 #include "components/os_crypt/os_crypt_mocker.h"
 #include "components/password_manager/core/browser/mock_field_info_store.h"
@@ -2070,11 +2072,19 @@
 }
 
 TEST_F(ChromeBrowsingDataRemoverDelegateTest, ZeroSuggestCacheClear) {
+  const std::string page_url = "https://google.com/search?q=chrome";
+  const std::string response = R"(["", ["foo", "bar"]])";
+
   PrefService* prefs = GetProfile()->GetPrefs();
-  omnibox::SetUserPreferenceForZeroSuggestCachedResponse(
-      prefs, "https://google.com/search?q=chrome", R"(["", ["foo", "bar"]])");
-  prefs->SetString(omnibox::kZeroSuggestCachedResults,
-                   R"(["", ["foo", "bar"]])");
+  omnibox::SetUserPreferenceForZeroSuggestCachedResponse(prefs, page_url,
+                                                         response);
+  omnibox::SetUserPreferenceForZeroSuggestCachedResponse(prefs, "", response);
+
+  ZeroSuggestCacheService* zero_suggest_cache_service =
+      ZeroSuggestCacheServiceFactory::GetForProfile(GetProfile());
+  zero_suggest_cache_service->StoreZeroSuggestResponse(page_url, response);
+  zero_suggest_cache_service->StoreZeroSuggestResponse("", response);
+
   BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(),
                                 content::BrowsingDataRemover::DATA_TYPE_COOKIES,
                                 false);
@@ -2083,6 +2093,7 @@
   EXPECT_TRUE(prefs->GetString(omnibox::kZeroSuggestCachedResults).empty());
   EXPECT_TRUE(
       prefs->GetDict(omnibox::kZeroSuggestCachedResultsWithURL).empty());
+  EXPECT_TRUE(zero_suggest_cache_service->IsCacheEmpty());
   EXPECT_EQ(content::BrowsingDataRemover::DATA_TYPE_COOKIES, GetRemovalMask());
   EXPECT_EQ(content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
             GetOriginTypeMask());
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index d74bd47..fe425a59 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -256,9 +256,9 @@
 #include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.h"
 #include "chrome/browser/ui/webui/ash/audio/audio.mojom.h"
 #include "chrome/browser/ui/webui/ash/audio/audio_ui.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader.mojom.h"
@@ -1270,8 +1270,8 @@
 
   if (ash::features::IsUploadOfficeToCloudEnabled()) {
     RegisterWebUIControllerInterfaceBinder<
-        chromeos::cloud_upload::mojom::PageHandlerFactory,
-        chromeos::cloud_upload::CloudUploadUI>(map);
+        ash::cloud_upload::mojom::PageHandlerFactory,
+        ash::cloud_upload::CloudUploadUI>(map);
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 55b12d8..8640ce2 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -148,7 +148,6 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_value_store.h"
-#include "components/prefs/scoped_user_pref_update.h"
 #include "components/site_isolation/site_isolation_policy.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
@@ -402,10 +401,8 @@
     // Clear kProfilesLastActive since the user only wants to launch a specific
     // profile. Don't clear it if the user launched a web app, in order to not
     // break any subsequent multi-profile session restore.
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          prefs::kProfilesLastActive);
-    base::Value* profile_list = update.Get();
-    profile_list->ClearList();
+    g_browser_process->local_state()->SetList(prefs::kProfilesLastActive,
+                                              base::Value::List());
   }
 
   StartupProfileInfo profile_info;
diff --git a/chrome/browser/chrome_origin_trials_browsertest.cc b/chrome/browser/chrome_origin_trials_browsertest.cc
index ff65ad3..ef1bfaa 100644
--- a/chrome/browser/chrome_origin_trials_browsertest.cc
+++ b/chrome/browser/chrome_origin_trials_browsertest.cc
@@ -9,7 +9,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/embedder_support/origin_trials/pref_names.h"
 #include "components/embedder_support/switches.h"
-#include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -57,23 +57,22 @@
   }
 
   void AddDisabledFeaturesToPrefs(const std::vector<std::string>& features) {
-    base::Value disabled_feature_list(base::Value::Type::LIST);
+    base::Value::List disabled_feature_list;
     for (const std::string& feature : features) {
       disabled_feature_list.Append(feature);
     }
-    ListPrefUpdate update(
-        local_state(), embedder_support::prefs::kOriginTrialDisabledFeatures);
-    *update = std::move(disabled_feature_list);
+    local_state()->SetList(
+        embedder_support::prefs::kOriginTrialDisabledFeatures,
+        std::move(disabled_feature_list));
   }
 
   void AddDisabledTokensToPrefs(const std::vector<std::string>& tokens) {
-    base::Value disabled_token_list(base::Value::Type::LIST);
+    base::Value::List disabled_token_list;
     for (const std::string& token : tokens) {
       disabled_token_list.Append(token);
     }
-    ListPrefUpdate update(local_state(),
-                          embedder_support::prefs::kOriginTrialDisabledTokens);
-    *update = std::move(disabled_token_list);
+    local_state()->SetList(embedder_support::prefs::kOriginTrialDisabledTokens,
+                           std::move(disabled_token_list));
   }
 
   PrefService* local_state() { return g_browser_process->local_state(); }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 79ae02b..274f02ff 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -68,9 +68,7 @@
   ]
 
   deps = [
-    ":device_configuration_proto",
     ":dlp_policy_event_proto",
-    ":login_logout_event_proto",
     "../ash/guest_os:guest_os_diagnostics_mojom",
     "//apps",
     "//ash",
@@ -599,471 +597,6 @@
   ]
 
   sources = [
-    "../ash/login/active_directory_migration_utils.cc",
-    "../ash/login/active_directory_migration_utils.h",
-    "../ash/login/app_mode/kiosk_launch_controller.cc",
-    "../ash/login/app_mode/kiosk_launch_controller.h",
-    "../ash/login/auth/chrome_cryptohome_authenticator.cc",
-    "../ash/login/auth/chrome_cryptohome_authenticator.h",
-    "../ash/login/auth/chrome_login_performer.cc",
-    "../ash/login/auth/chrome_login_performer.h",
-    "../ash/login/auth/chrome_safe_mode_delegate.cc",
-    "../ash/login/auth/chrome_safe_mode_delegate.h",
-    "../ash/login/challenge_response_auth_keys_loader.cc",
-    "../ash/login/challenge_response_auth_keys_loader.h",
-    "../ash/login/chrome_restart_request.cc",
-    "../ash/login/chrome_restart_request.h",
-    "../ash/login/configuration_keys.cc",
-    "../ash/login/configuration_keys.h",
-    "../ash/login/consolidated_consent_field_trial.cc",
-    "../ash/login/consolidated_consent_field_trial.h",
-    "../ash/login/demo_mode/demo_extensions_external_loader.cc",
-    "../ash/login/demo_mode/demo_extensions_external_loader.h",
-    "../ash/login/demo_mode/demo_mode_resources_remover.cc",
-    "../ash/login/demo_mode/demo_mode_resources_remover.h",
-    "../ash/login/demo_mode/demo_resources.cc",
-    "../ash/login/demo_mode/demo_resources.h",
-    "../ash/login/demo_mode/demo_session.cc",
-    "../ash/login/demo_mode/demo_session.h",
-    "../ash/login/demo_mode/demo_setup_controller.cc",
-    "../ash/login/demo_mode/demo_setup_controller.h",
-    "../ash/login/easy_unlock/chrome_proximity_auth_client.cc",
-    "../ash/login/easy_unlock/chrome_proximity_auth_client.h",
-    "../ash/login/easy_unlock/easy_unlock_auth_attempt.cc",
-    "../ash/login/easy_unlock/easy_unlock_auth_attempt.h",
-    "../ash/login/easy_unlock/easy_unlock_challenge_wrapper.cc",
-    "../ash/login/easy_unlock/easy_unlock_challenge_wrapper.h",
-    "../ash/login/easy_unlock/easy_unlock_create_keys_operation.cc",
-    "../ash/login/easy_unlock/easy_unlock_create_keys_operation.h",
-    "../ash/login/easy_unlock/easy_unlock_get_keys_operation.cc",
-    "../ash/login/easy_unlock/easy_unlock_get_keys_operation.h",
-    "../ash/login/easy_unlock/easy_unlock_key_manager.cc",
-    "../ash/login/easy_unlock/easy_unlock_key_manager.h",
-    "../ash/login/easy_unlock/easy_unlock_key_names.cc",
-    "../ash/login/easy_unlock/easy_unlock_key_names.h",
-    "../ash/login/easy_unlock/easy_unlock_metrics.cc",
-    "../ash/login/easy_unlock/easy_unlock_metrics.h",
-    "../ash/login/easy_unlock/easy_unlock_notification_controller.cc",
-    "../ash/login/easy_unlock/easy_unlock_notification_controller.h",
-    "../ash/login/easy_unlock/easy_unlock_refresh_keys_operation.cc",
-    "../ash/login/easy_unlock/easy_unlock_refresh_keys_operation.h",
-    "../ash/login/easy_unlock/easy_unlock_remove_keys_operation.cc",
-    "../ash/login/easy_unlock/easy_unlock_remove_keys_operation.h",
-    "../ash/login/easy_unlock/easy_unlock_service.cc",
-    "../ash/login/easy_unlock/easy_unlock_service.h",
-    "../ash/login/easy_unlock/easy_unlock_service_factory.cc",
-    "../ash/login/easy_unlock/easy_unlock_service_factory.h",
-    "../ash/login/easy_unlock/easy_unlock_service_regular.cc",
-    "../ash/login/easy_unlock/easy_unlock_service_regular.h",
-    "../ash/login/easy_unlock/easy_unlock_service_signin.cc",
-    "../ash/login/easy_unlock/easy_unlock_service_signin.h",
-    "../ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc",
-    "../ash/login/easy_unlock/easy_unlock_tpm_key_manager.h",
-    "../ash/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc",
-    "../ash/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h",
-    "../ash/login/easy_unlock/easy_unlock_types.cc",
-    "../ash/login/easy_unlock/easy_unlock_types.h",
-    "../ash/login/easy_unlock/easy_unlock_user_login_flow.cc",
-    "../ash/login/easy_unlock/easy_unlock_user_login_flow.h",
-    "../ash/login/easy_unlock/smartlock_feature_usage_metrics.cc",
-    "../ash/login/easy_unlock/smartlock_feature_usage_metrics.h",
-    "../ash/login/easy_unlock/smartlock_state_handler.cc",
-    "../ash/login/easy_unlock/smartlock_state_handler.h",
-    "../ash/login/enrollment/auto_enrollment_check_screen.cc",
-    "../ash/login/enrollment/auto_enrollment_check_screen.h",
-    "../ash/login/enrollment/auto_enrollment_check_screen_view.h",
-    "../ash/login/enrollment/auto_enrollment_controller.cc",
-    "../ash/login/enrollment/auto_enrollment_controller.h",
-    "../ash/login/enrollment/enrollment_screen.cc",
-    "../ash/login/enrollment/enrollment_screen.h",
-    "../ash/login/enrollment/enrollment_screen_view.h",
-    "../ash/login/enrollment/enrollment_uma.cc",
-    "../ash/login/enrollment/enrollment_uma.h",
-    "../ash/login/enrollment/enterprise_enrollment_helper.cc",
-    "../ash/login/enrollment/enterprise_enrollment_helper.h",
-    "../ash/login/enrollment/enterprise_enrollment_helper_impl.cc",
-    "../ash/login/enrollment/enterprise_enrollment_helper_impl.h",
-    "../ash/login/enterprise_user_session_metrics.cc",
-    "../ash/login/enterprise_user_session_metrics.h",
-    "../ash/login/error_screens_histogram_helper.cc",
-    "../ash/login/error_screens_histogram_helper.h",
-    "../ash/login/existing_user_controller.cc",
-    "../ash/login/existing_user_controller.h",
-    "../ash/login/extensions/login_screen_extensions_content_script_manager.cc",
-    "../ash/login/extensions/login_screen_extensions_content_script_manager.h",
-    "../ash/login/extensions/login_screen_extensions_content_script_manager_factory.cc",
-    "../ash/login/extensions/login_screen_extensions_content_script_manager_factory.h",
-    "../ash/login/extensions/login_screen_extensions_lifetime_manager.cc",
-    "../ash/login/extensions/login_screen_extensions_lifetime_manager.h",
-    "../ash/login/extensions/login_screen_extensions_lifetime_manager_factory.cc",
-    "../ash/login/extensions/login_screen_extensions_lifetime_manager_factory.h",
-    "../ash/login/gaia_reauth_token_fetcher.cc",
-    "../ash/login/gaia_reauth_token_fetcher.h",
-    "../ash/login/hats_unlock_survey_trigger.cc",
-    "../ash/login/hats_unlock_survey_trigger.h",
-    "../ash/login/help_app_launcher.cc",
-    "../ash/login/help_app_launcher.h",
-    "../ash/login/helper.cc",
-    "../ash/login/helper.h",
-    "../ash/login/hwid_checker.cc",
-    "../ash/login/hwid_checker.h",
-    "../ash/login/lock/screen_locker.cc",
-    "../ash/login/lock/screen_locker.h",
-    "../ash/login/lock/views_screen_locker.cc",
-    "../ash/login/lock/views_screen_locker.h",
-    "../ash/login/lock_screen_utils.cc",
-    "../ash/login/lock_screen_utils.h",
-    "../ash/login/login_auth_recorder.cc",
-    "../ash/login/login_auth_recorder.h",
-    "../ash/login/login_client_cert_usage_observer.cc",
-    "../ash/login/login_client_cert_usage_observer.h",
-    "../ash/login/login_pref_names.cc",
-    "../ash/login/login_pref_names.h",
-    "../ash/login/login_screen_extensions_storage_cleaner.cc",
-    "../ash/login/login_screen_extensions_storage_cleaner.h",
-    "../ash/login/login_wizard.h",
-    "../ash/login/marketing_backend_connector.cc",
-    "../ash/login/marketing_backend_connector.h",
-    "../ash/login/mojo_system_info_dispatcher.cc",
-    "../ash/login/mojo_system_info_dispatcher.h",
-    "../ash/login/onboarding_user_activity_counter.cc",
-    "../ash/login/onboarding_user_activity_counter.h",
-    "../ash/login/oobe_configuration.cc",
-    "../ash/login/oobe_configuration.h",
-    "../ash/login/oobe_screen.cc",
-    "../ash/login/oobe_screen.h",
-    "../ash/login/profile_auth_data.cc",
-    "../ash/login/profile_auth_data.h",
-    "../ash/login/quick_unlock/auth_token.cc",
-    "../ash/login/quick_unlock/auth_token.h",
-    "../ash/login/quick_unlock/fake_pin_salt_storage.cc",
-    "../ash/login/quick_unlock/fake_pin_salt_storage.h",
-    "../ash/login/quick_unlock/fingerprint_power_button_race_detector.cc",
-    "../ash/login/quick_unlock/fingerprint_power_button_race_detector.h",
-    "../ash/login/quick_unlock/fingerprint_storage.cc",
-    "../ash/login/quick_unlock/fingerprint_storage.h",
-    "../ash/login/quick_unlock/fingerprint_utils.cc",
-    "../ash/login/quick_unlock/fingerprint_utils.h",
-    "../ash/login/quick_unlock/pin_backend.cc",
-    "../ash/login/quick_unlock/pin_backend.h",
-    "../ash/login/quick_unlock/pin_salt_storage.cc",
-    "../ash/login/quick_unlock/pin_salt_storage.h",
-    "../ash/login/quick_unlock/pin_storage_cryptohome.cc",
-    "../ash/login/quick_unlock/pin_storage_cryptohome.h",
-    "../ash/login/quick_unlock/pin_storage_prefs.cc",
-    "../ash/login/quick_unlock/pin_storage_prefs.h",
-    "../ash/login/quick_unlock/quick_unlock_factory.cc",
-    "../ash/login/quick_unlock/quick_unlock_factory.h",
-    "../ash/login/quick_unlock/quick_unlock_storage.cc",
-    "../ash/login/quick_unlock/quick_unlock_storage.h",
-    "../ash/login/quick_unlock/quick_unlock_utils.cc",
-    "../ash/login/quick_unlock/quick_unlock_utils.h",
-    "../ash/login/reauth_stats.cc",
-    "../ash/login/reauth_stats.h",
-    "../ash/login/reporting/login_logout_reporter.cc",
-    "../ash/login/reporting/login_logout_reporter.h",
-    "../ash/login/saml/in_session_password_change_manager.cc",
-    "../ash/login/saml/in_session_password_change_manager.h",
-    "../ash/login/saml/in_session_password_sync_manager.cc",
-    "../ash/login/saml/in_session_password_sync_manager.h",
-    "../ash/login/saml/in_session_password_sync_manager_factory.cc",
-    "../ash/login/saml/in_session_password_sync_manager_factory.h",
-    "../ash/login/saml/password_change_success_notification.cc",
-    "../ash/login/saml/password_change_success_notification.h",
-    "../ash/login/saml/password_expiry_notification.cc",
-    "../ash/login/saml/password_expiry_notification.h",
-    "../ash/login/saml/password_sync_token_checkers_collection.cc",
-    "../ash/login/saml/password_sync_token_checkers_collection.h",
-    "../ash/login/saml/password_sync_token_fetcher.cc",
-    "../ash/login/saml/password_sync_token_fetcher.h",
-    "../ash/login/saml/password_sync_token_login_checker.cc",
-    "../ash/login/saml/password_sync_token_login_checker.h",
-    "../ash/login/saml/password_sync_token_verifier.cc",
-    "../ash/login/saml/password_sync_token_verifier.h",
-    "../ash/login/saml/password_sync_token_verifier_factory.cc",
-    "../ash/login/saml/password_sync_token_verifier_factory.h",
-    "../ash/login/saml/public_saml_url_fetcher.cc",
-    "../ash/login/saml/public_saml_url_fetcher.h",
-    "../ash/login/saml/saml_metric_utils.cc",
-    "../ash/login/saml/saml_metric_utils.h",
-    "../ash/login/saml/saml_profile_prefs.cc",
-    "../ash/login/saml/saml_profile_prefs.h",
-    "../ash/login/screen_manager.cc",
-    "../ash/login/screen_manager.h",
-    "../ash/login/screens/active_directory_login_screen.cc",
-    "../ash/login/screens/active_directory_login_screen.h",
-    "../ash/login/screens/active_directory_password_change_screen.cc",
-    "../ash/login/screens/active_directory_password_change_screen.h",
-    "../ash/login/screens/app_downloading_screen.cc",
-    "../ash/login/screens/app_downloading_screen.h",
-    "../ash/login/screens/arc_terms_of_service_screen.cc",
-    "../ash/login/screens/arc_terms_of_service_screen.h",
-    "../ash/login/screens/assistant_optin_flow_screen.cc",
-    "../ash/login/screens/assistant_optin_flow_screen.h",
-    "../ash/login/screens/base_screen.cc",
-    "../ash/login/screens/base_screen.h",
-    "../ash/login/screens/chrome_user_selection_screen.cc",
-    "../ash/login/screens/chrome_user_selection_screen.h",
-    "../ash/login/screens/chromevox_hint/chromevox_hint_detector.cc",
-    "../ash/login/screens/chromevox_hint/chromevox_hint_detector.h",
-    "../ash/login/screens/consolidated_consent_screen.cc",
-    "../ash/login/screens/consolidated_consent_screen.h",
-    "../ash/login/screens/cryptohome_recovery_screen.cc",
-    "../ash/login/screens/cryptohome_recovery_screen.h",
-    "../ash/login/screens/demo_preferences_screen.cc",
-    "../ash/login/screens/demo_preferences_screen.h",
-    "../ash/login/screens/demo_setup_screen.cc",
-    "../ash/login/screens/demo_setup_screen.h",
-    "../ash/login/screens/device_disabled_screen.cc",
-    "../ash/login/screens/device_disabled_screen.h",
-    "../ash/login/screens/edu_coexistence_login_screen.cc",
-    "../ash/login/screens/edu_coexistence_login_screen.h",
-    "../ash/login/screens/enable_adb_sideloading_screen.cc",
-    "../ash/login/screens/enable_adb_sideloading_screen.h",
-    "../ash/login/screens/enable_debugging_screen.cc",
-    "../ash/login/screens/enable_debugging_screen.h",
-    "../ash/login/screens/encryption_migration_mode.h",
-    "../ash/login/screens/encryption_migration_screen.cc",
-    "../ash/login/screens/encryption_migration_screen.h",
-    "../ash/login/screens/error_screen.cc",
-    "../ash/login/screens/error_screen.h",
-    "../ash/login/screens/eula_screen.cc",
-    "../ash/login/screens/eula_screen.h",
-    "../ash/login/screens/family_link_notice_screen.cc",
-    "../ash/login/screens/family_link_notice_screen.h",
-    "../ash/login/screens/fingerprint_setup_screen.cc",
-    "../ash/login/screens/fingerprint_setup_screen.h",
-    "../ash/login/screens/gaia_password_changed_screen.cc",
-    "../ash/login/screens/gaia_password_changed_screen.h",
-    "../ash/login/screens/gaia_screen.cc",
-    "../ash/login/screens/gaia_screen.h",
-    "../ash/login/screens/gesture_navigation_screen.cc",
-    "../ash/login/screens/gesture_navigation_screen.h",
-    "../ash/login/screens/guest_tos_screen.cc",
-    "../ash/login/screens/guest_tos_screen.h",
-    "../ash/login/screens/hardware_data_collection_screen.cc",
-    "../ash/login/screens/hardware_data_collection_screen.h",
-    "../ash/login/screens/hid_detection_screen.cc",
-    "../ash/login/screens/hid_detection_screen.h",
-    "../ash/login/screens/kiosk_autolaunch_screen.cc",
-    "../ash/login/screens/kiosk_autolaunch_screen.h",
-    "../ash/login/screens/kiosk_enable_screen.cc",
-    "../ash/login/screens/kiosk_enable_screen.h",
-    "../ash/login/screens/lacros_data_backward_migration_screen.cc",
-    "../ash/login/screens/lacros_data_backward_migration_screen.h",
-    "../ash/login/screens/lacros_data_migration_screen.cc",
-    "../ash/login/screens/lacros_data_migration_screen.h",
-    "../ash/login/screens/local_state_error_screen.cc",
-    "../ash/login/screens/local_state_error_screen.h",
-    "../ash/login/screens/locale_switch_screen.cc",
-    "../ash/login/screens/locale_switch_screen.h",
-    "../ash/login/screens/management_transition_screen.cc",
-    "../ash/login/screens/management_transition_screen.h",
-    "../ash/login/screens/marketing_opt_in_screen.cc",
-    "../ash/login/screens/marketing_opt_in_screen.h",
-    "../ash/login/screens/multidevice_setup_screen.cc",
-    "../ash/login/screens/multidevice_setup_screen.h",
-    "../ash/login/screens/network_error.cc",
-    "../ash/login/screens/network_error.h",
-    "../ash/login/screens/network_screen.cc",
-    "../ash/login/screens/network_screen.h",
-    "../ash/login/screens/offline_login_screen.cc",
-    "../ash/login/screens/offline_login_screen.h",
-    "../ash/login/screens/os_install_screen.cc",
-    "../ash/login/screens/os_install_screen.h",
-    "../ash/login/screens/os_trial_screen.cc",
-    "../ash/login/screens/os_trial_screen.h",
-    "../ash/login/screens/packaged_license_screen.cc",
-    "../ash/login/screens/packaged_license_screen.h",
-    "../ash/login/screens/parental_handoff_screen.cc",
-    "../ash/login/screens/parental_handoff_screen.h",
-    "../ash/login/screens/pin_setup_screen.cc",
-    "../ash/login/screens/pin_setup_screen.h",
-    "../ash/login/screens/quick_start_screen.cc",
-    "../ash/login/screens/quick_start_screen.h",
-    "../ash/login/screens/recommend_apps/fake_recommend_apps_fetcher.cc",
-    "../ash/login/screens/recommend_apps/fake_recommend_apps_fetcher.h",
-    "../ash/login/screens/recommend_apps/recommend_apps_fetcher.cc",
-    "../ash/login/screens/recommend_apps/recommend_apps_fetcher.h",
-    "../ash/login/screens/recommend_apps/recommend_apps_fetcher_delegate.h",
-    "../ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc",
-    "../ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.h",
-    "../ash/login/screens/recommend_apps_screen.cc",
-    "../ash/login/screens/recommend_apps_screen.h",
-    "../ash/login/screens/reset_screen.cc",
-    "../ash/login/screens/reset_screen.h",
-    "../ash/login/screens/saml_confirm_password_screen.cc",
-    "../ash/login/screens/saml_confirm_password_screen.h",
-    "../ash/login/screens/signin_fatal_error_screen.cc",
-    "../ash/login/screens/signin_fatal_error_screen.h",
-    "../ash/login/screens/smart_privacy_protection_screen.cc",
-    "../ash/login/screens/smart_privacy_protection_screen.h",
-    "../ash/login/screens/sync_consent_screen.cc",
-    "../ash/login/screens/sync_consent_screen.h",
-    "../ash/login/screens/terms_of_service_screen.cc",
-    "../ash/login/screens/terms_of_service_screen.h",
-    "../ash/login/screens/theme_selection_screen.cc",
-    "../ash/login/screens/theme_selection_screen.h",
-    "../ash/login/screens/tpm_error_screen.cc",
-    "../ash/login/screens/tpm_error_screen.h",
-    "../ash/login/screens/update_required_screen.cc",
-    "../ash/login/screens/update_required_screen.h",
-    "../ash/login/screens/update_screen.cc",
-    "../ash/login/screens/update_screen.h",
-    "../ash/login/screens/user_creation_screen.cc",
-    "../ash/login/screens/user_creation_screen.h",
-    "../ash/login/screens/user_selection_screen.cc",
-    "../ash/login/screens/user_selection_screen.h",
-    "../ash/login/screens/welcome_screen.cc",
-    "../ash/login/screens/welcome_screen.h",
-    "../ash/login/screens/wrong_hwid_screen.cc",
-    "../ash/login/screens/wrong_hwid_screen.h",
-    "../ash/login/security_token_pin_dialog_host_login_impl.cc",
-    "../ash/login/security_token_pin_dialog_host_login_impl.h",
-    "../ash/login/security_token_session_controller.cc",
-    "../ash/login/security_token_session_controller.h",
-    "../ash/login/security_token_session_controller_factory.cc",
-    "../ash/login/security_token_session_controller_factory.h",
-    "../ash/login/session/chrome_session_manager.cc",
-    "../ash/login/session/chrome_session_manager.h",
-    "../ash/login/session/user_session_initializer.cc",
-    "../ash/login/session/user_session_initializer.h",
-    "../ash/login/session/user_session_manager.cc",
-    "../ash/login/session/user_session_manager.h",
-    "../ash/login/signin/auth_error_observer.cc",
-    "../ash/login/signin/auth_error_observer.h",
-    "../ash/login/signin/auth_error_observer_factory.cc",
-    "../ash/login/signin/auth_error_observer_factory.h",
-    "../ash/login/signin/merge_session_navigation_throttle.cc",
-    "../ash/login/signin/merge_session_navigation_throttle.h",
-    "../ash/login/signin/merge_session_throttling_utils.cc",
-    "../ash/login/signin/merge_session_throttling_utils.h",
-    "../ash/login/signin/oauth2_login_manager.cc",
-    "../ash/login/signin/oauth2_login_manager.h",
-    "../ash/login/signin/oauth2_login_manager_factory.cc",
-    "../ash/login/signin/oauth2_login_manager_factory.h",
-    "../ash/login/signin/oauth2_login_verifier.cc",
-    "../ash/login/signin/oauth2_login_verifier.h",
-    "../ash/login/signin/oauth2_token_fetcher.cc",
-    "../ash/login/signin/oauth2_token_fetcher.h",
-    "../ash/login/signin/oauth2_token_initializer.cc",
-    "../ash/login/signin/oauth2_token_initializer.h",
-    "../ash/login/signin/offline_signin_limiter.cc",
-    "../ash/login/signin/offline_signin_limiter.h",
-    "../ash/login/signin/offline_signin_limiter_factory.cc",
-    "../ash/login/signin/offline_signin_limiter_factory.h",
-    "../ash/login/signin/signin_error_notifier.cc",
-    "../ash/login/signin/signin_error_notifier.h",
-    "../ash/login/signin/signin_error_notifier_factory.cc",
-    "../ash/login/signin/signin_error_notifier_factory.h",
-    "../ash/login/signin/token_handle_fetcher.cc",
-    "../ash/login/signin/token_handle_fetcher.h",
-    "../ash/login/signin/token_handle_util.cc",
-    "../ash/login/signin/token_handle_util.h",
-    "../ash/login/signin_partition_manager.cc",
-    "../ash/login/signin_partition_manager.h",
-    "../ash/login/signin_specifics.h",
-    "../ash/login/startup_utils.cc",
-    "../ash/login/startup_utils.h",
-    "../ash/login/ui/captive_portal_dialog_delegate.cc",
-    "../ash/login/ui/captive_portal_dialog_delegate.h",
-    "../ash/login/ui/captive_portal_view.cc",
-    "../ash/login/ui/captive_portal_view.h",
-    "../ash/login/ui/captive_portal_window_proxy.cc",
-    "../ash/login/ui/captive_portal_window_proxy.h",
-    "../ash/login/ui/input_events_blocker.cc",
-    "../ash/login/ui/input_events_blocker.h",
-    "../ash/login/ui/kiosk_app_menu_controller.cc",
-    "../ash/login/ui/kiosk_app_menu_controller.h",
-    "../ash/login/ui/login_display.cc",
-    "../ash/login/ui/login_display.h",
-    "../ash/login/ui/login_display_host.cc",
-    "../ash/login/ui/login_display_host.h",
-    "../ash/login/ui/login_display_host_common.cc",
-    "../ash/login/ui/login_display_host_common.h",
-    "../ash/login/ui/login_display_host_mojo.cc",
-    "../ash/login/ui/login_display_host_mojo.h",
-    "../ash/login/ui/login_display_host_webui.cc",
-    "../ash/login/ui/login_display_host_webui.h",
-    "../ash/login/ui/login_display_mojo.cc",
-    "../ash/login/ui/login_display_mojo.h",
-    "../ash/login/ui/login_display_webui.cc",
-    "../ash/login/ui/login_display_webui.h",
-    "../ash/login/ui/login_feedback.cc",
-    "../ash/login/ui/login_feedback.h",
-    "../ash/login/ui/login_screen_extension_ui/create_options.cc",
-    "../ash/login/ui/login_screen_extension_ui/create_options.h",
-    "../ash/login/ui/login_screen_extension_ui/dialog_delegate.cc",
-    "../ash/login/ui/login_screen_extension_ui/dialog_delegate.h",
-    "../ash/login/ui/login_screen_extension_ui/web_dialog_view.cc",
-    "../ash/login/ui/login_screen_extension_ui/web_dialog_view.h",
-    "../ash/login/ui/login_screen_extension_ui/window.cc",
-    "../ash/login/ui/login_screen_extension_ui/window.h",
-    "../ash/login/ui/login_web_dialog.cc",
-    "../ash/login/ui/login_web_dialog.h",
-    "../ash/login/ui/oobe_dialog_size_utils.cc",
-    "../ash/login/ui/oobe_dialog_size_utils.h",
-    "../ash/login/ui/oobe_ui_dialog_delegate.cc",
-    "../ash/login/ui/oobe_ui_dialog_delegate.h",
-    "../ash/login/ui/signin_ui.h",
-    "../ash/login/ui/simple_web_view_dialog.cc",
-    "../ash/login/ui/simple_web_view_dialog.h",
-    "../ash/login/ui/user_adding_screen.cc",
-    "../ash/login/ui/user_adding_screen.h",
-    "../ash/login/ui/user_adding_screen_input_methods_controller.cc",
-    "../ash/login/ui/user_adding_screen_input_methods_controller.h",
-    "../ash/login/ui/views/user_board_view.h",
-    "../ash/login/ui/web_contents_forced_title.cc",
-    "../ash/login/ui/web_contents_forced_title.h",
-    "../ash/login/ui/webui_login_view.cc",
-    "../ash/login/ui/webui_login_view.h",
-    "../ash/login/user_board_view_mojo.cc",
-    "../ash/login/user_board_view_mojo.h",
-    "../ash/login/user_flow.cc",
-    "../ash/login/user_flow.h",
-    "../ash/login/user_online_signin_notifier.cc",
-    "../ash/login/user_online_signin_notifier.h",
-    "../ash/login/users/affiliation.cc",
-    "../ash/login/users/affiliation.h",
-    "../ash/login/users/avatar/user_image_file_selector.cc",
-    "../ash/login/users/avatar/user_image_file_selector.h",
-    "../ash/login/users/avatar/user_image_loader.cc",
-    "../ash/login/users/avatar/user_image_loader.h",
-    "../ash/login/users/avatar/user_image_manager.cc",
-    "../ash/login/users/avatar/user_image_manager.h",
-    "../ash/login/users/avatar/user_image_manager_impl.cc",
-    "../ash/login/users/avatar/user_image_manager_impl.h",
-    "../ash/login/users/avatar/user_image_sync_observer.cc",
-    "../ash/login/users/avatar/user_image_sync_observer.h",
-    "../ash/login/users/chrome_user_manager.cc",
-    "../ash/login/users/chrome_user_manager.h",
-    "../ash/login/users/chrome_user_manager_impl.cc",
-    "../ash/login/users/chrome_user_manager_impl.h",
-    "../ash/login/users/chrome_user_manager_util.cc",
-    "../ash/login/users/chrome_user_manager_util.h",
-    "../ash/login/users/default_user_image/default_user_images.cc",
-    "../ash/login/users/default_user_image/default_user_images.h",
-    "../ash/login/users/multi_profile_user_controller.cc",
-    "../ash/login/users/multi_profile_user_controller.h",
-    "../ash/login/users/multi_profile_user_controller_delegate.h",
-    "../ash/login/users/scoped_test_user_manager.cc",
-    "../ash/login/users/scoped_test_user_manager.h",
-    "../ash/login/users/supervised_user_manager.h",
-    "../ash/login/users/supervised_user_manager_impl.cc",
-    "../ash/login/users/supervised_user_manager_impl.h",
-    "../ash/login/users/test_users.cc",
-    "../ash/login/users/test_users.h",
-    "../ash/login/users/user_manager_interface.h",
-    "../ash/login/version_info_updater.cc",
-    "../ash/login/version_info_updater.h",
-    "../ash/login/version_updater/update_time_estimator.cc",
-    "../ash/login/version_updater/update_time_estimator.h",
-    "../ash/login/version_updater/version_updater.cc",
-    "../ash/login/version_updater/version_updater.h",
-    "../ash/login/wizard_context.cc",
-    "../ash/login/wizard_context.h",
-    "../ash/login/wizard_controller.cc",
-    "../ash/login/wizard_controller.h",
     "app_mode/app_session.cc",
     "app_mode/app_session.h",
     "app_mode/app_session_browser_window_handler.cc",
@@ -2678,30 +2211,10 @@
   ]
 }
 
-proto_library("device_configuration_proto") {
-  sources = [ "../ash/login/screens/recommend_apps/device_configuration.proto" ]
-  generate_python = false
-}
-
 proto_library("dlp_policy_event_proto") {
   sources = [ "policy/dlp/dlp_policy_event.proto" ]
 }
 
-proto_library("login_logout_event_proto") {
-  sources =
-      [ "../policy/messaging_layer/proto/synced/login_logout_event.proto" ]
-  deps = [
-    "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
-  ]
-}
-
-proto_library("lock_unlock_event_proto") {
-  sources = [ "../policy/messaging_layer/proto/synced/lock_unlock_event.proto" ]
-  deps = [
-    "//chrome/browser/policy/messaging_layer/proto:session_affiliated_user",
-  ]
-}
-
 proto_library("user_event_reporter_testing_record_proto") {
   sources =
       [ "../ash/policy/reporting/user_event_reporter_testing_record.proto" ]
diff --git a/chrome/browser/chromeos/extensions/default_app_order.cc b/chrome/browser/chromeos/extensions/default_app_order.cc
index b08c094b..676aed7 100644
--- a/chrome/browser/chromeos/extensions/default_app_order.cc
+++ b/chrome/browser/chromeos/extensions/default_app_order.cc
@@ -248,7 +248,7 @@
       ReadExternalOrdinalFile(ordinals_file);
   if (ordinals_value) {
     std::string locale = g_browser_process->GetApplicationLocale();
-    for (const base::Value& i : ordinals_value->GetListDeprecated()) {
+    for (const base::Value& i : ordinals_value->GetList()) {
       if (i.is_string()) {
         std::string app_id = i.GetString();
         app_ids_.push_back(app_id);
diff --git a/chrome/browser/download/notification/multi_profile_download_notifier.cc b/chrome/browser/download/notification/multi_profile_download_notifier.cc
index fe38d34..e40f191a 100644
--- a/chrome/browser/download/notification/multi_profile_download_notifier.cc
+++ b/chrome/browser/download/notification/multi_profile_download_notifier.cc
@@ -91,10 +91,8 @@
     content::DownloadManager* manager) {
   client_->OnManagerGoingDown(manager);
 
-  auto it = std::find_if(download_notifiers_.begin(), download_notifiers_.end(),
-                         [manager](const auto& notifier) {
-                           return notifier->GetManager() == manager;
-                         });
+  auto it = base::ranges::find(download_notifiers_, manager,
+                               &download::AllDownloadItemNotifier::GetManager);
   DCHECK(it != download_notifiers_.end());
   download_notifiers_.erase(it);
 }
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
index 5e515fe..62244f8 100644
--- a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
+++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
@@ -4,10 +4,10 @@
 
 #include "chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h"
 
-#include <algorithm>
 #include <memory>
 
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "chrome/browser/enterprise/connectors/analysis/analysis_settings.h"
@@ -337,9 +337,9 @@
     DVLOG(1) << "FinishRequest key=" << key << " not active";
   }
 
-  auto it2 = std::find_if(
-      pending_requests_.begin(), pending_requests_.end(),
-      [key](const RequestInfo& info) { return key == info.request.get(); });
+  auto it2 = base::ranges::find(
+      pending_requests_, key,
+      [](const RequestInfo& info) { return info.request.get(); });
   if (it2 != pending_requests_.end())
     pending_requests_.erase(it2);
 }
diff --git a/chrome/browser/extensions/system_display/display_info_provider_mac.cc b/chrome/browser/extensions/system_display/display_info_provider_mac.cc
index 5349ca2..afdd893c 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_mac.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_mac.cc
@@ -13,7 +13,7 @@
 
 void DisplayInfoProviderMac::UpdateDisplayUnitInfoForPlatform(
     const display::Display& display,
-    extensions::api::system_display::DisplayUnitInfo* unit) {
+    extensions::api::system_display::DisplayUnitInfo* unit) const {
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
diff --git a/chrome/browser/extensions/system_display/display_info_provider_mac.h b/chrome/browser/extensions/system_display/display_info_provider_mac.h
index 9d0968a9..e87f2a98 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_mac.h
+++ b/chrome/browser/extensions/system_display/display_info_provider_mac.h
@@ -19,7 +19,7 @@
   // DisplayInfoProvider implementation.
   void UpdateDisplayUnitInfoForPlatform(
       const display::Display& display,
-      api::system_display::DisplayUnitInfo* unit) override;
+      api::system_display::DisplayUnitInfo* unit) const override;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/system_display/display_info_provider_win.cc b/chrome/browser/extensions/system_display/display_info_provider_win.cc
index d1bedd0..6c95c42a 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_win.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_win.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/extensions/system_display/display_info_provider_win.h"
 
+#include <memory>
+#include <utility>
+
 #include <stddef.h>
 #include <windows.h>
 
@@ -56,9 +59,9 @@
 
 void DisplayInfoProviderWin::UpdateDisplayUnitInfoForPlatform(
     const display::Display& display,
-    extensions::api::system_display::DisplayUnitInfo* unit) {
+    extensions::api::system_display::DisplayUnitInfo* unit) const {
   DisplayUnitInfoList all_displays;
-  EnumDisplayMonitors(NULL, NULL, EnumMonitorCallback,
+  EnumDisplayMonitors(nullptr, nullptr, EnumMonitorCallback,
                       reinterpret_cast<LPARAM>(&all_displays));
   for (size_t i = 0; i < all_displays.size(); ++i) {
     if (unit->id == all_displays[i].id) {
diff --git a/chrome/browser/extensions/system_display/display_info_provider_win.h b/chrome/browser/extensions/system_display/display_info_provider_win.h
index 636089d..e5b6c4e 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_win.h
+++ b/chrome/browser/extensions/system_display/display_info_provider_win.h
@@ -19,7 +19,7 @@
   // DisplayInfoProvider implementation.
   void UpdateDisplayUnitInfoForPlatform(
       const display::Display& display,
-      api::system_display::DisplayUnitInfo* unit) override;
+      api::system_display::DisplayUnitInfo* unit) const override;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/feed/android/BUILD.gn b/chrome/browser/feed/android/BUILD.gn
index 970275c..0e96b84 100644
--- a/chrome/browser/feed/android/BUILD.gn
+++ b/chrome/browser/feed/android/BUILD.gn
@@ -293,6 +293,7 @@
     "java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediatorTest.java",
     "java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementCoordinatorTest.java",
     "java/src/org/chromium/chrome/browser/feed/followmanagement/FollowManagementMediatorTest.java",
+    "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawableTest.java",
     "java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderViewTest.java",
     "java/src/org/chromium/chrome/browser/feed/sort_ui/FeedOptionsCoordinatorTest.java",
     "java/src/org/chromium/chrome/browser/feed/webfeed/TestWebFeedFaviconFetcher.java",
diff --git a/chrome/browser/feed/android/java/res/values/dimens.xml b/chrome/browser/feed/android/java/res/values/dimens.xml
index 089fe33..67089d9 100644
--- a/chrome/browser/feed/android/java/res/values/dimens.xml
+++ b/chrome/browser/feed/android/java/res/values/dimens.xml
@@ -57,7 +57,7 @@
     <!-- Feed unread dot dimens -->
     <dimen name="feed_badge_radius">2.5dp</dimen>
     <dimen name="feed_badge_hoffset">-1dp</dimen>
-    <dimen name="feed_badge_voffset">2dp</dimen>
+    <dimen name="feed_badge_voffset">-0.5dp</dimen>
 
     <!-- Dynamic color dimensions. -->
     <dimen name="feed_header_section_tab_bg_elevation_enabled">@dimen/default_elevation_2</dimen>
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
index 9aae886..8173fa9a 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
@@ -53,6 +53,20 @@
                 && !Profile.getLastUsedRegularProfile().isChild();
     }
 
+    public static boolean shouldUseWebFeedAwarenessIPH() {
+        return ChromeFeatureList
+                .getFieldTrialParamByFeature(
+                        ChromeFeatureList.WEB_FEED_AWARENESS, "awareness_style")
+                .equals("IPH");
+    }
+
+    public static boolean shouldUseNewIndicator() {
+        return ChromeFeatureList
+                .getFieldTrialParamByFeature(
+                        ChromeFeatureList.WEB_FEED_AWARENESS, "awareness_style")
+                .equals("new_animation");
+    }
+
     /**
      * @return Whether the feed should automatically scroll down when it first loads so that the
      *         first card is at the top of the screen. This is for use with screenshot utilities.
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java
index 12f86aca..0ffef8ba 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawable.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.feed.sections;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
@@ -14,6 +17,7 @@
 import android.view.View;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.widget.AppCompatTextView;
 
 import com.google.android.material.shape.MaterialShapeDrawable;
@@ -24,24 +28,33 @@
 /**
  * Drawable representing the unread dot.
  *
- * Allows for setting of text and animating the width when text changes.
+ * Allows for setting of text inside the dot and animating the width when text changes.
  */
 public class SectionHeaderBadgeDrawable extends Drawable {
-    private Paint mPaint;
-    // Only used for calculating text width in dps and to easily apply text appearances.
-    private AppCompatTextView mTextView;
-    private MaterialShapeDrawable mShapeDrawable;
+    private static final int ANIMATION_DURATION_MS = 400;
+    private static final int ANIMATION_START_DELAY_MS = 500;
+
+    private final Paint mPaint;
+    private final MaterialShapeDrawable mShapeDrawable;
+    private final Context mContext;
+    // Default text size.
+    private final float mTextSize;
+
     private String mText;
-    private Context mContext;
+    private ValueAnimator mAnimator;
+    private View mAnchor;
+    private boolean mHasPendingAnimation;
 
     public SectionHeaderBadgeDrawable(Context context) {
         mContext = context;
 
         // This textview is only used to easily set the text parameters and calculate textual width.
-        mTextView = new AppCompatTextView(context);
-        mTextView.setTextAppearance(context, R.style.TextAppearance_Material3_LabelSmall);
-        mPaint = mTextView.getPaint();
+        AppCompatTextView textView = new AppCompatTextView(context);
+        textView.setTextAppearance(context, R.style.TextAppearance_Material3_LabelSmall);
+        mPaint = textView.getPaint();
         mPaint.setTextAlign(Paint.Align.CENTER);
+        mPaint.setColor(SemanticColorUtils.getDefaultTextColorOnAccent1(context));
+        mTextSize = mPaint.getTextSize();
 
         mShapeDrawable = new MaterialShapeDrawable();
         mShapeDrawable.setCornerSize(
@@ -51,9 +64,47 @@
         mText = "";
     }
 
+    /**
+     * Sets the text showing inside the dot.
+     *
+     * If we are currently attached to an anchor and the text is not empty, we will show
+     * the text and start the animation back into a text-less dot after 500ms. Otherwise, we will
+     * perform the animation back into a text-less dot 500ms after attaching to an anchor.
+     *
+     * @param text The text to show inside the dot.
+     */
     public void setText(String text) {
-        mText = text;
-        mTextView.setText(text);
+        // Cast null to empty string first.
+        final String finalText = (text == null) ? "" : text;
+        // Do nothing if no change.
+        if (mText.equals(finalText)) return;
+        mText = finalText;
+        // Text -> empty animation. Otherwise, don't animate.
+        if (!mText.isEmpty()) {
+            // Stop previous animation if there is one.
+            if (mAnimator != null && mAnimator.isStarted()) {
+                mAnimator.pause();
+            }
+            // Don't animate if we aren't attached to an anchor.
+            // Set pending animation to true so that we will start the
+            // pending animation after attaching to an anchor.
+            if (mAnchor == null) {
+                mHasPendingAnimation = true;
+                return;
+            }
+            mHasPendingAnimation = false;
+            setUpAndRunAnimation();
+        } else {
+            // Restore alpha and text sizes, in case we had an animation before.
+            mPaint.setAlpha(255);
+            mPaint.setTextSize(mTextSize);
+            mHasPendingAnimation = false; // Turn off any pending animation.
+            // Recalculate bounds and redraw if we are attached.
+            if (mAnchor != null) {
+                setBounds(calculateBounds(mAnchor, mText));
+                invalidateSelf();
+            }
+        }
     }
 
     @Override
@@ -69,7 +120,7 @@
         // Draws the full text with the top left corner at (centerX, centerY-(halfheight of text)).
         // We define halfheight as the average of ascent and descent to ensure the text does not
         // appear lopsided even if the font changes.
-        canvas.drawText(mText, 0, mText.length(), bounds.centerX(),
+        canvas.drawText(mText, bounds.centerX(),
                 bounds.centerY() - ((mPaint.descent() + mPaint.ascent()) / 2), mPaint);
     }
 
@@ -85,29 +136,10 @@
         invalidateSelf();
     }
 
-    /**
-     * Called when we are attaching the drawable to a new overlay.
-     *
-     * @param anchorBounds the bounding box for the overlay.
-     */
     @Override
-    public void setBounds(Rect anchorBounds) {
-        // CenterY is the top of the bounding box + a custom vertical offset.
-        int centerY = anchorBounds.top
-                + mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_voffset);
-        int halfHeight = mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_radius);
-        // HalfWidth is the radius if no text, or the text width/2.
-        int halfWidth = Math.max(halfHeight, mTextView.getMeasuredWidth() / 2);
-        // CenterX is the right side of the bounding box + radius + offset.
-        int centerX = anchorBounds.right + halfWidth
-                - mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_hoffset);
-        // The new bounds for the dot + any text to be rendered therein.
-        Rect newBounds = new Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth,
-                centerY + halfHeight);
-        // We don't set bounding box for the textview because
-        // one does not set bounds for views, and it's not part of the drawing.
-        mShapeDrawable.setBounds(newBounds);
-        super.setBounds(newBounds);
+    public void setBounds(Rect bounds) {
+        mShapeDrawable.setBounds(bounds);
+        super.setBounds(bounds);
     }
 
     @Override
@@ -115,16 +147,116 @@
         return mPaint.getAlpha();
     }
 
+    /**
+     * Attaches the drawable to an anchor.
+     *
+     * Does not do anything if we are already attached to this anchor. Otherwise,
+     * triggers a draw loop which will put this drawable in the top right corner of the anchor.
+     * @param anchor View to anchor this Drawable to.
+     */
     public void attach(View anchor) {
-        Rect badgeBounds = new Rect();
-        anchor.getDrawingRect(badgeBounds);
-        setBounds(badgeBounds);
+        // Do not re-attach to same anchor view. Otherwise, this forces a layout
+        // that messes up any ongoing animation.
+        if (mAnchor != null && mAnchor.equals(anchor)) {
+            invalidateSelf();
+            return;
+        }
+        mAnchor = anchor;
+        setBounds(calculateBounds(anchor, mText));
         anchor.getOverlay().add(this);
         invalidateSelf();
+        // If we have a pending animation, set it up and run it..
+        if (mHasPendingAnimation) {
+            mHasPendingAnimation = false;
+            setUpAndRunAnimation();
+        }
+    }
+
+    /**
+     * Detaches the drawable from the anchor.
+     *
+     * Does nothing if we are not attached to this anchor.
+     * @param anchor View anchor that we previously called attach on.
+     */
+    public void detach(View anchor) {
+        if (mAnchor == null || !mAnchor.equals(anchor)) {
+            return;
+        }
+        mAnchor = null;
+        anchor.getOverlay().remove(this);
+        invalidateSelf();
     }
 
-    public void detach(View anchor) {
-        anchor.getOverlay().remove(this);
-        invalidateSelf();
+    /**
+     * Calculates the bounds for this drawable if we were anchored to this view and
+     * contains this text. Does not actually modify anything.
+     *
+     * @param anchor The view we are hypothetically anchored to.
+     * @param text The text we hypothetically should display.
+     * @return The bounds we should have in order to show in the top right corner.
+     */
+    private Rect calculateBounds(View anchor, String text) {
+        Rect anchorBounds = new Rect();
+        anchor.getDrawingRect(anchorBounds);
+
+        Rect textBounds = new Rect();
+        mPaint.getTextBounds(text, 0, text.length(), textBounds);
+
+        int radius = mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_radius);
+        // HalfHeight is radius if no text, or text height / 2.
+        int halfHeight = Math.max(radius, (textBounds.bottom - textBounds.top) / 2 + radius);
+        // HalfWidth is the radius if no text, or the text width/2.
+        int halfWidth = Math.max(radius, (textBounds.right - textBounds.left) / 2 + radius);
+
+        // CenterY is the top of the bounding box + a custom vertical offset.
+        int centerY = anchorBounds.top
+                + mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_voffset)
+                + halfHeight;
+        // CenterX is the right side of the bounding box + radius + offset.
+        int centerX = anchorBounds.right + halfWidth
+                - mContext.getResources().getDimensionPixelSize(R.dimen.feed_badge_hoffset);
+
+        // The new bounds for the dot + any text to be rendered therein.
+        return new Rect(centerX - halfWidth, centerY - halfHeight, centerX + halfWidth,
+                centerY + halfHeight);
+    }
+
+    /**
+     * Sets up and starts an animation which would turn us from a badge containing text
+     * back into a dot not containing any text.
+     *
+     * The animation will have a 500ms delay before beginning.
+     */
+    private void setUpAndRunAnimation() {
+        mAnimator = ValueAnimator.ofInt(0, 100);
+        Rect bounds = getBounds();
+        Rect toBounds = calculateBounds(mAnchor, "");
+        mAnimator.addUpdateListener((ValueAnimator animation) -> {
+            float fraction = animation.getAnimatedFraction();
+            Rect newBounds =
+                    new Rect((int) (bounds.left - (bounds.left - toBounds.left) * fraction),
+                            (int) (bounds.top - (bounds.top - toBounds.top) * fraction),
+                            (int) (bounds.right - (bounds.right - toBounds.right) * fraction),
+                            (int) (bounds.bottom - (bounds.bottom - toBounds.bottom) * fraction));
+            mPaint.setAlpha((int) (255 - 255 * fraction));
+            mPaint.setTextSize(mTextSize - mTextSize * fraction);
+            setBounds(newBounds);
+            invalidateSelf();
+        });
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                mText = "";
+                invalidateSelf();
+            }
+        });
+        mAnimator.setStartDelay(ANIMATION_START_DELAY_MS);
+        mAnimator.setDuration(ANIMATION_DURATION_MS);
+        mAnimator.start();
+    }
+
+    @VisibleForTesting
+    boolean getHasPendingAnimationForTest() {
+        return mHasPendingAnimation;
     }
 }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawableTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawableTest.java
new file mode 100644
index 0000000..524e045
--- /dev/null
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderBadgeDrawableTest.java
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.feed.sections;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+@RunWith(BaseRobolectricTestRunner.class)
+public class SectionHeaderBadgeDrawableTest {
+    private Activity mActivity;
+    private SectionHeaderBadgeDrawable mDrawable;
+
+    @Before
+    public void setUp() {
+        mActivity = Robolectric.setupActivity(Activity.class);
+        mActivity.setTheme(org.chromium.chrome.R.style.Theme_MaterialComponents);
+
+        mDrawable = new SectionHeaderBadgeDrawable(mActivity);
+    }
+
+    @Test
+    public void pendingAnimationWhenNonNullTextAndNotAttached() {
+        mDrawable.setText("new");
+        assertTrue(mDrawable.getHasPendingAnimationForTest());
+    }
+
+    @Test
+    public void pendingAnimationStartsFalse() {
+        assertFalse(mDrawable.getHasPendingAnimationForTest());
+    }
+
+    @Test
+    public void pendingAnimationFalseForNoTextChange() {
+        mDrawable.setText(null);
+        assertFalse(mDrawable.getHasPendingAnimationForTest());
+    }
+
+    @Test
+    public void pendingAnimationFalseForNullText() {
+        mDrawable.setText("new");
+        mDrawable.setText(null);
+        assertFalse(mDrawable.getHasPendingAnimationForTest());
+    }
+}
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderProperties.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderProperties.java
index e1ec2a6..848cc1e 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderProperties.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderProperties.java
@@ -22,11 +22,13 @@
             new PropertyModel.WritableObjectPropertyKey<>();
     public static final PropertyModel.WritableBooleanPropertyKey OPTIONS_INDICATOR_IS_OPEN_KEY =
             new WritableBooleanPropertyKey();
+    public static final PropertyModel.WritableObjectPropertyKey<String> BADGE_TEXT_KEY =
+            new PropertyModel.WritableObjectPropertyKey<>();
 
     public static PropertyModel createSectionHeader(String headerText) {
         return new PropertyModel
                 .Builder(HEADER_TEXT_KEY, UNREAD_CONTENT_KEY, OPTIONS_INDICATOR_VISIBILITY_KEY,
-                        OPTIONS_INDICATOR_IS_OPEN_KEY)
+                        OPTIONS_INDICATOR_IS_OPEN_KEY, BADGE_TEXT_KEY)
                 .with(HEADER_TEXT_KEY, headerText)
                 .with(UNREAD_CONTENT_KEY, false)
                 .build();
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 cad6b84..0d7384b 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
@@ -110,6 +110,8 @@
         // Null when unread indicator isn't shown.
         @Nullable
         public UnreadIndicator unreadIndicator;
+        // The text to show on the unreadIndicator, if any.
+        public String unreadIndicatorText;
         // The tab's displayed text.
         public String text = "";
     }
@@ -218,12 +220,12 @@
      * Set the properties for the header tab at a particular index to text.
      *
      * Does nothing if index is invalid. Make sure to call addTab() beforehand.
-     *
      * @param text Text to set the tab to.
      * @param hasUnreadContent Whether there is unread content.
+     * @param unreadContentText
      * @param index Index of the tab to set.
      */
-    void setHeaderAt(String text, boolean hasUnreadContent, int index) {
+    void setHeaderAt(String text, boolean hasUnreadContent, String unreadContentText, int index) {
         TabLayout.Tab tab = getTabAt(index);
         if (tab == null) {
             return;
@@ -232,6 +234,7 @@
 
         state.text = text;
         state.hasUnreadContent = hasUnreadContent;
+        state.unreadIndicatorText = unreadContentText;
         applyTabState(tab);
     }
 
@@ -505,6 +508,7 @@
                             new UnreadIndicator(tab.view.findViewById(android.R.id.text1));
                 }
             }
+            state.unreadIndicator.mNewBadge.setText(state.unreadIndicatorText);
         } else {
             if (state.unreadIndicator != null) {
                 state.unreadIndicator.destroy();
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderViewBinder.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderViewBinder.java
index bbf2e727..685fc747 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderViewBinder.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/sections/SectionHeaderViewBinder.java
@@ -86,7 +86,8 @@
             SectionHeaderView view, int index, int count, PropertyKey payload) {
         PropertyModel header = headers.get(0);
         if (payload == null || payload == SectionHeaderProperties.HEADER_TEXT_KEY
-                || payload == SectionHeaderProperties.UNREAD_CONTENT_KEY) {
+                || payload == SectionHeaderProperties.UNREAD_CONTENT_KEY
+                || payload == SectionHeaderProperties.BADGE_TEXT_KEY) {
             // Only use 1st tab for legacy headerText;
             view.setHeaderText(header.get(SectionHeaderProperties.HEADER_TEXT_KEY));
 
@@ -95,8 +96,8 @@
                 PropertyModel tabModel = headers.get(i);
                 boolean hasUnreadContent = tabModel.get(SectionHeaderProperties.UNREAD_CONTENT_KEY);
 
-                view.setHeaderAt(
-                        tabModel.get(SectionHeaderProperties.HEADER_TEXT_KEY), hasUnreadContent, i);
+                view.setHeaderAt(tabModel.get(SectionHeaderProperties.HEADER_TEXT_KEY),
+                        hasUnreadContent, tabModel.get(SectionHeaderProperties.BADGE_TEXT_KEY), i);
             }
         }
         if (payload == null
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 35847bf7..23fc51f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5447,6 +5447,13 @@
     "expiry_milestone": 108
   },
   {
+    "name": "private-aggregation-debug-mode",
+    "owners": [
+      "//content/browser/private_aggregation/OWNERS"
+    ],
+    "expiry_milestone": 111
+  },
+  {
     "name": "private-network-access-preflight-short-timeout",
     "owners": [ "lyf", "chrome-security-owp-team@google.com" ],
     "expiry_milestone": 107
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 70d2219..fc0ab9d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2418,6 +2418,13 @@
     "Enables an updated Privacy Sandbox UI. Also enables some related "
     "features.";
 
+const char kPrivateAggregationDebugModeName[] =
+    "Private Aggregation debug mode";
+const char kPrivateAggregationDebugModeDescription[] =
+    "Enables debug mode for the Private Aggregation API. This removes all "
+    "reporting delays. Only works if the Private Aggregation API is already "
+    "enabled.";
+
 const char kProminentDarkModeActiveTabTitleName[] =
     "Prominent Dark Mode Active Tab Titles";
 const char kProminentDarkModeActiveTabTitleDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index ef5cd6c..892c9c6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1350,6 +1350,9 @@
 extern const char kPrivacySandboxV3Name[];
 extern const char kPrivacySandboxV3Description[];
 
+extern const char kPrivateAggregationDebugModeName[];
+extern const char kPrivateAggregationDebugModeDescription[];
+
 extern const char kProminentDarkModeActiveTabTitleName[];
 extern const char kProminentDarkModeActiveTabTitleDescription[];
 
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index 0d24b93..5a0fac18 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -164,6 +164,23 @@
   return nullptr;
 }
 
+bool ShowProfilePickerIfNeeded(bool incognito) {
+  if (ProfilePicker::ShouldShowAtLaunch() &&
+      chrome::GetTotalBrowserCount() == 0 && !incognito) {
+    // Profile picker does not support passing through the incognito param. It
+    // also does not support passing through the
+    // `should_trigger_session_restore` param but that's very common (left
+    // clicking the launcher icon) so we can't skip the picker in this case. The
+    // default behavior for the first browser window supports session restore,
+    // additional windows are opened blank and thus it works reasonably well for
+    // BrowserServiceLacros.
+    ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
+        ProfilePicker::EntryPoint::kNewSessionOnExistingProcess));
+    return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 // A struct to keep the pending OpenUrl task.
@@ -232,21 +249,10 @@
 void BrowserServiceLacros::NewWindow(bool incognito,
                                      bool should_trigger_session_restore,
                                      NewWindowCallback callback) {
-  if (ProfilePicker::ShouldShowAtLaunch() &&
-      chrome::GetTotalBrowserCount() == 0 && !incognito) {
-    // Profile picker does not support passing through the incognito param. It
-    // also does not support passing through the
-    // `should_trigger_session_restore` param but that's very common (left
-    // clicking the launcher icon) so we can't skip the picker in this case. The
-    // default behavior for the first browser window supports session restore,
-    // additional windows are opened blank and thus it works reasonably well for
-    // BrowserServiceLacros.
-    ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
-        ProfilePicker::EntryPoint::kNewSessionOnExistingProcess));
+  if (ShowProfilePickerIfNeeded(incognito)) {
     std::move(callback).Run();
     return;
   }
-
   LoadMainProfile(
       base::BindOnce(&BrowserServiceLacros::NewWindowWithProfile,
                      weak_ptr_factory_.GetWeakPtr(), incognito,
@@ -292,19 +298,28 @@
 
 void BrowserServiceLacros::NewTab(bool should_trigger_session_restore,
                                   NewTabCallback callback) {
-  if (ProfilePicker::ShouldShowAtLaunch() &&
-      chrome::GetTotalBrowserCount() == 0) {
-    // The first browser window will trigger session restore if needed.
-    ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
-        ProfilePicker::EntryPoint::kNewSessionOnExistingProcess));
+  if (ShowProfilePickerIfNeeded(false)) {
     std::move(callback).Run();
     return;
   }
-
   LoadMainProfile(
-      base::BindOnce(&BrowserServiceLacros::NewTabWithProfile,
+      base::BindOnce(&BrowserServiceLacros::LaunchOrNewTabWithProfile,
                      weak_ptr_factory_.GetWeakPtr(),
-                     should_trigger_session_restore, std::move(callback)),
+                     should_trigger_session_restore, std::move(callback),
+                     /*is_new_tab=*/true),
+      /*can_trigger_fre=*/true);
+}
+
+void BrowserServiceLacros::Launch(LaunchCallback callback) {
+  if (ShowProfilePickerIfNeeded(false)) {
+    std::move(callback).Run();
+    return;
+  }
+  LoadMainProfile(
+      base::BindOnce(&BrowserServiceLacros::LaunchOrNewTabWithProfile,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     /*should_trigger_session_restore=*/true,
+                     std::move(callback), /*is_new_tab=*/false),
       /*can_trigger_fre=*/true);
 }
 
@@ -625,9 +640,10 @@
                           platform_window->GetWindowUniqueId());
 }
 
-void BrowserServiceLacros::NewTabWithProfile(
+void BrowserServiceLacros::LaunchOrNewTabWithProfile(
     bool should_trigger_session_restore,
     NewTabCallback callback,
+    bool is_new_tab,
     Profile* profile) {
   if (!profile) {
     LOG(WARNING) << "No profile, it might be an early exit from the FRE. "
@@ -636,17 +652,27 @@
     return;
   }
 
-  Browser* browser;
-  {
-    chrome::ScopedTabbedBrowserDisplayer displayer(
-        profile, should_trigger_session_restore);
-    browser = displayer.browser();
-    if (browser)
-      chrome::NewTab(browser);
-  }
-  if (browser)
+  Browser* browser =
+      chrome::FindTabbedBrowser(profile, /*match_original_profiles=*/false);
+  if (browser != nullptr) {
+    chrome::NewTab(browser);
     browser->SetFocusToLocationBar();
-  std::move(callback).Run();
+  } else {
+    DCHECK(is_new_tab || should_trigger_session_restore);
+    if (is_new_tab && should_trigger_session_restore) {
+      // Session restore happens asynchronously. Let |OnSessionRestored| create
+      // a new tab afterwards.
+      using OpenUrlParams = crosapi::mojom::OpenUrlParams;
+      auto params = OpenUrlParams::New();
+      params->disposition = OpenUrlParams::WindowOpenDisposition::kSwitchToTab;
+      pending_open_urls_.push_back(
+          PendingOpenUrl{profile, GURL{chrome::kChromeUINewTabURL},
+                         std::move(params), std::move(callback)});
+    }
+    chrome::NewEmptyWindow(profile, should_trigger_session_restore);
+  }
+  if (!callback.is_null())
+    std::move(callback).Run();
 }
 
 void BrowserServiceLacros::OpenUrlWithProfile(
diff --git a/chrome/browser/lacros/browser_service_lacros.h b/chrome/browser/lacros/browser_service_lacros.h
index 2936cad..0d4d8dad 100644
--- a/chrome/browser/lacros/browser_service_lacros.h
+++ b/chrome/browser/lacros/browser_service_lacros.h
@@ -47,6 +47,7 @@
       NewWindowForDetachingTabCallback callback) override;
   void NewTab(bool should_trigger_session_restore,
               NewTabCallback callback) override;
+  void Launch(LaunchCallback callback) override;
   void OpenUrl(const GURL& url,
                crosapi::mojom::OpenUrlParamsPtr params,
                OpenUrlCallback callback) override;
@@ -89,9 +90,10 @@
       const std::u16string& group_id,
       NewWindowForDetachingTabCallback callback,
       Profile* profile);
-  void NewTabWithProfile(bool should_trigger_session_restore,
-                         NewTabCallback callback,
-                         Profile* profile);
+  void LaunchOrNewTabWithProfile(bool should_trigger_session_restore,
+                                 NewTabCallback callback,
+                                 bool is_new_tab,
+                                 Profile* profile);
   void OpenUrlWithProfile(const GURL& url,
                           crosapi::mojom::OpenUrlParamsPtr params,
                           OpenUrlCallback callback,
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 9a425d3..0d13192 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -386,11 +386,11 @@
   auto* new_tab_strip = new_browser->tab_strip_model();
   ASSERT_EQ(3, new_tab_strip->count());
 
-  EXPECT_EQ("",  // The new tab.
-            new_tab_strip->GetWebContentsAt(0)->GetLastCommittedURL().path());
   EXPECT_EQ("/title1.html",
-            new_tab_strip->GetWebContentsAt(1)->GetLastCommittedURL().path());
+            new_tab_strip->GetWebContentsAt(0)->GetLastCommittedURL().path());
   EXPECT_EQ("/title2.html",
+            new_tab_strip->GetWebContentsAt(1)->GetLastCommittedURL().path());
+  EXPECT_EQ("",  // The new tab.
             new_tab_strip->GetWebContentsAt(2)->GetLastCommittedURL().path());
 
   // A second call to NewTab() ignores session restore and adds yet another new
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
index 2acdce7..5bca6de 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
@@ -79,10 +79,9 @@
   if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported())
     return;
 
-  DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
-  auto& origin_id_dict = update->GetDict();
-  origin_id_dict.Set(kExpirableToken,
-                     base::TimeToValue(base::Time::Now() + kExpirationDelta));
+  ScopedDictPrefUpdate update(pref_service, kMediaDrmOriginIds);
+  update->Set(kExpirableToken,
+              base::TimeToValue(base::Time::Now() + kExpirationDelta));
 }
 
 void RemoveExpirableToken(base::Value::Dict& origin_id_dict) {
@@ -140,10 +139,9 @@
 base::UnguessableToken TakeFirstOriginId(PrefService* const pref_service) {
   DVLOG(3) << __func__;
 
-  DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
-  base::Value::Dict& origin_id_dict = update->GetDict();
+  ScopedDictPrefUpdate update(pref_service, kMediaDrmOriginIds);
 
-  base::Value::List* origin_ids = origin_id_dict.FindList(kOriginIds);
+  base::Value::List* origin_ids = update->FindList(kOriginIds);
   if (!origin_ids)
     return base::UnguessableToken::Null();
 
@@ -365,8 +363,8 @@
 
   // On devices that need to, check that the user has recently requested
   // an origin ID. If not, then skip pre-provisioning on those devices.
-  DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
-  if (!CanPreProvision(update->GetDict())) {
+  ScopedDictPrefUpdate update(pref_service_, kMediaDrmOriginIds);
+  if (!CanPreProvision(*update)) {
     // Disable any network monitoring, if it exists.
     network_observer_.reset();
     return;
@@ -374,8 +372,7 @@
 
   // No need to pre-provision if there are already enough existing
   // pre-provisioned origin IDs.
-  if (CountAvailableOriginIds(update->GetDict()) >=
-      kMaxPreProvisionedOriginIds) {
+  if (CountAvailableOriginIds(*update) >= kMaxPreProvisionedOriginIds) {
     // Disable any network monitoring, if it exists.
     network_observer_.reset();
     return;
@@ -494,15 +491,14 @@
              origin_id);
     pending_provisioned_origin_id_cbs_.pop();
   } else {
-    DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
-    AddOriginId(update->GetDict(), origin_id.value());
+    ScopedDictPrefUpdate update(pref_service_, kMediaDrmOriginIds);
+    AddOriginId(*update, origin_id.value());
 
     // If we already have enough pre-provisioned origin IDs, we're done.
     // Stop watching for network change events.
-    if (CountAvailableOriginIds(update->GetDict()) >=
-        kMaxPreProvisionedOriginIds) {
+    if (CountAvailableOriginIds(*update) >= kMaxPreProvisionedOriginIds) {
       network_observer_.reset();
-      RemoveExpirableToken(update->GetDict());
+      RemoveExpirableToken(*update);
       is_provisioning_ = false;
       return;
     }
diff --git a/chrome/browser/media/cdm_document_service_impl_test.cc b/chrome/browser/media/cdm_document_service_impl_test.cc
index fa72a8d..bed4242 100644
--- a/chrome/browser/media/cdm_document_service_impl_test.cc
+++ b/chrome/browser/media/cdm_document_service_impl_test.cc
@@ -115,13 +115,13 @@
     entry.SetKey(kOriginId, base::UnguessableTokenToValue(
                                 base::UnguessableToken::Create()));
 
-    DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
-    base::Value* dict = update.Get();
+    ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+    base::Value::Dict& dict = update.Get();
     const std::string serialized_origin = web_contents()
                                               ->GetPrimaryMainFrame()
                                               ->GetLastCommittedOrigin()
                                               .Serialize();
-    dict->SetKey(serialized_origin, std::move(entry));
+    dict.Set(serialized_origin, std::move(entry));
   }
 
  protected:
diff --git a/chrome/browser/media/cdm_pref_service_helper.cc b/chrome/browser/media/cdm_pref_service_helper.cc
index f3a080a..a8bdaf3 100644
--- a/chrome/browser/media/cdm_pref_service_helper.cc
+++ b/chrome/browser/media/cdm_pref_service_helper.cc
@@ -174,10 +174,10 @@
     const base::RepeatingCallback<bool(const GURL&)>& filter) {
   DVLOG(1) << __func__ << " From [" << start << ", " << end << "]";
 
-  DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+  ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
 
   std::vector<std::string> origins_to_delete;
-  for (auto key_value : update->DictItems()) {
+  for (auto key_value : *update) {
     const std::string& origin = key_value.first;
 
     // Null filter indicates that we should delete everything.
@@ -211,7 +211,7 @@
 
   // Remove CDM preference data.
   for (const auto& origin_str : origins_to_delete)
-    update->RemoveKey(origin_str);
+    update->Remove(origin_str);
 
   DVLOG(1) << __func__ << "Done removing CDM preference data";
 }
@@ -244,12 +244,11 @@
   // Create an new entry or overwrite the existing one in case we weren't able
   // to get a valid origin ID from `FromDictValue()`.
   if (!cdm_pref_data) {
-    DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
-    base::Value* update_dict = update.Get();
+    ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
 
     cdm_pref_data = std::make_unique<CdmPrefData>(
         base::UnguessableToken::Create(), base::Time::Now());
-    update_dict->SetKey(serialized_cdm_origin, ToDictValue(*cdm_pref_data));
+    update->Set(serialized_cdm_origin, ToDictValue(*cdm_pref_data));
   }
 
   return cdm_pref_data;
@@ -268,8 +267,8 @@
   const std::string serialized_cdm_origin = cdm_origin.Serialize();
   DCHECK(!serialized_cdm_origin.empty());
 
-  DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
-  base::Value::Dict& dict = update->GetDict();
+  ScopedDictPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+  base::Value::Dict& dict = update.Get();
 
   base::Value::Dict* dict_value = dict.FindDict(serialized_cdm_origin);
   if (!dict_value) {
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index c9763f7..84f48fda 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -67,9 +67,9 @@
 // Set the user preference for |origin| to prefer tab mirroring.
 void EnableTabMirroringForOrigin(PrefService* prefs,
                                  const std::string& origin) {
-  ListPrefUpdate update(prefs,
-                        media_router::prefs::kMediaRouterTabMirroringSources);
-  base::Value::List& list = update->GetList();
+  ScopedListPrefUpdate update(
+      prefs, media_router::prefs::kMediaRouterTabMirroringSources);
+  base::Value::List& list = update.Get();
   if (!base::Contains(list, base::Value(origin)))
     list.Append(origin);
 }
@@ -798,9 +798,9 @@
 
   // Remove the user preference for |origin|.
   {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kMediaRouterTabMirroringSources);
-    update->GetList().EraseValue(base::Value(origin));
+    ScopedListPrefUpdate update(profile()->GetPrefs(),
+                                prefs::kMediaRouterTabMirroringSources);
+    update->EraseValue(base::Value(origin));
   }
 
   // Auto-join requests should now go through.
@@ -854,10 +854,10 @@
 
   // Remove the user preference for |origin| in OffTheRecord.
   {
-    ListPrefUpdate update(
+    ScopedListPrefUpdate update(
         profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)->GetPrefs(),
         prefs::kMediaRouterTabMirroringSources);
-    update->GetList().EraseValue(base::Value(origin));
+    update->EraseValue(base::Value(origin));
   }
 
   // Auto-join requests should now go through.
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.h b/chrome/browser/navigation_predictor/anchor_element_preloader.h
index 86fae78..95b7fdc 100644
--- a/chrome/browser/navigation_predictor/anchor_element_preloader.h
+++ b/chrome/browser/navigation_predictor/anchor_element_preloader.h
@@ -23,13 +23,13 @@
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class AnchorPreloadingFailureReason {
-  // Numbering starts from `kPreloadingFailureReasonCommonEnd` defined in
+  // Numbering starts from `kPreloadingFailureReasonContentEnd` defined in
   // //content/public/preloading.h . Advance numbering by +1 when adding a new
   // element.
 
   // The number of allowed anchor element preloading attempts has been exceeded.
   kLimitExceeded = static_cast<int>(
-      content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd),
+      content::PreloadingFailureReason::kPreloadingFailureReasonContentEnd),
 };
 
 // Helper function to convert AnchorPreloadingFailureReason to
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
index 12d18276..9945909b 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
@@ -145,8 +145,8 @@
     SearchPrefetchServingReason reason) {
   return static_cast<content::PreloadingFailureReason>(
       static_cast<int>(reason) +
-      static_cast<int>(
-          content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+      static_cast<int>(content::PreloadingFailureReason::
+                           kPreloadingFailureReasonContentEnd));
 }
 
 }  // namespace
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
index aeb885d2..2ff4b3e6 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
@@ -244,8 +244,8 @@
     SearchPrefetchServingReason reason) {
   return static_cast<content::PreloadingFailureReason>(
       static_cast<int>(reason) +
-      static_cast<int>(
-          content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+      static_cast<int>(content::PreloadingFailureReason::
+                           kPreloadingFailureReasonContentEnd));
 }
 
 class SearchPrefetchWithoutPrefetchingBrowserTest
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
index 12e3d001..f8fbab8 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_preload_unified_browsertest.cc
@@ -64,8 +64,8 @@
     PrerenderPredictionStatus status) {
   return static_cast<content::PreloadingFailureReason>(
       static_cast<int>(status) +
-      static_cast<int>(
-          content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+      static_cast<int>(content::PreloadingFailureReason::
+                           kPreloadingFailureReasonContentEnd));
 }
 
 // Sets up testing context for the search preloading features: search prefetch
diff --git a/chrome/browser/preloading/prerender/prerender_manager.cc b/chrome/browser/preloading/prerender/prerender_manager.cc
index bc2365b6..7cdc9b84 100644
--- a/chrome/browser/preloading/prerender/prerender_manager.cc
+++ b/chrome/browser/preloading/prerender/prerender_manager.cc
@@ -136,7 +136,7 @@
     return static_cast<content::PreloadingFailureReason>(
         static_cast<int>(status) +
         static_cast<int>(content::PreloadingFailureReason::
-                             kPreloadingFailureReasonCommonEnd));
+                             kPreloadingFailureReasonContentEnd));
   }
 
   void SetFailureReason(PrerenderPredictionStatus status) {
diff --git a/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc b/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
index aa4bbcc5..64d5a4a 100644
--- a/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
+++ b/chrome/browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc
@@ -67,8 +67,8 @@
     PrerenderPredictionStatus status) {
   return static_cast<content::PreloadingFailureReason>(
       static_cast<int>(status) +
-      static_cast<int>(
-          content::PreloadingFailureReason::kPreloadingFailureReasonCommonEnd));
+      static_cast<int>(content::PreloadingFailureReason::
+                           kPreloadingFailureReasonContentEnd));
 }
 
 class AutocompleteActionPredictorObserverImpl
diff --git a/chrome/browser/profile_resetter/brandcoded_default_settings.cc b/chrome/browser/profile_resetter/brandcoded_default_settings.cc
index 46828f90..9808b79 100644
--- a/chrome/browser/profile_resetter/brandcoded_default_settings.cc
+++ b/chrome/browser/profile_resetter/brandcoded_default_settings.cc
@@ -10,6 +10,7 @@
 #include "chrome/installer/util/initial_preferences_constants.h"
 #include "components/crx_file/id_util.h"
 #include "components/search_engines/search_engines_pref_names.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 BrandcodedDefaultSettings::BrandcodedDefaultSettings() {
 }
@@ -28,24 +29,20 @@
               << "Root item must be a dictionary.";
       return;
     }
-    master_dictionary_.reset(
-        static_cast<base::DictionaryValue*>(root.release()));
+    master_dictionary_ = std::move(*root).TakeDict();
   }
 }
 
 BrandcodedDefaultSettings::~BrandcodedDefaultSettings() {
 }
 
-std::unique_ptr<base::ListValue>
+absl::optional<base::Value::List>
 BrandcodedDefaultSettings::GetSearchProviderOverrides() const {
   return ExtractList(prefs::kSearchProviderOverrides);
 }
 
 bool BrandcodedDefaultSettings::GetHomepage(std::string* homepage) const {
-  if (!master_dictionary_)
-    return false;
-  const std::string* val =
-      master_dictionary_->GetDict().FindString(prefs::kHomePage);
+  const std::string* val = master_dictionary_.FindString(prefs::kHomePage);
   if (!val)
     return false;
   *homepage = *val;
@@ -53,25 +50,20 @@
 }
 
 absl::optional<bool> BrandcodedDefaultSettings::GetHomepageIsNewTab() const {
-  return master_dictionary_
-             ? master_dictionary_->FindBoolPath(prefs::kHomePageIsNewTabPage)
-             : absl::nullopt;
+  return master_dictionary_.FindBoolByDottedPath(prefs::kHomePageIsNewTabPage);
 }
 
 absl::optional<bool> BrandcodedDefaultSettings::GetShowHomeButton() const {
-  return master_dictionary_
-             ? master_dictionary_->FindBoolPath(prefs::kShowHomeButton)
-             : absl::nullopt;
+  return master_dictionary_.FindBoolByDottedPath(prefs::kShowHomeButton);
 }
 
 bool BrandcodedDefaultSettings::GetExtensions(
     std::vector<std::string>* extension_ids) const {
   DCHECK(extension_ids);
-  base::DictionaryValue* extensions = nullptr;
-  if (master_dictionary_ &&
-      master_dictionary_->GetDictionary(
-          installer::initial_preferences::kExtensionsBlock, &extensions)) {
-    for (const auto extension_id : extensions->GetDict()) {
+  const base::Value::Dict* extensions = master_dictionary_.FindDictByDottedPath(
+      installer::initial_preferences::kExtensionsBlock);
+  if (extensions) {
+    for (const auto extension_id : *extensions) {
       if (crx_file::id_util::IdIsValid(extension_id.first))
         extension_ids->push_back(extension_id.first);
     }
@@ -82,11 +74,8 @@
 
 bool BrandcodedDefaultSettings::GetRestoreOnStartup(
     int* restore_on_startup) const {
-  if (!master_dictionary_)
-    return false;
-
   absl::optional<int> maybe_restore_on_startup =
-      master_dictionary_->FindIntPath(prefs::kRestoreOnStartup);
+      master_dictionary_.FindIntByDottedPath(prefs::kRestoreOnStartup);
   if (!maybe_restore_on_startup)
     return false;
 
@@ -96,17 +85,17 @@
   return true;
 }
 
-std::unique_ptr<base::ListValue>
+absl::optional<base::Value::List>
 BrandcodedDefaultSettings::GetUrlsToRestoreOnStartup() const {
   return ExtractList(prefs::kURLsToRestoreOnStartup);
 }
 
-std::unique_ptr<base::ListValue> BrandcodedDefaultSettings::ExtractList(
+absl::optional<base::Value::List> BrandcodedDefaultSettings::ExtractList(
     const char* pref_name) const {
-  const base::ListValue* value = nullptr;
-  if (master_dictionary_ && master_dictionary_->GetList(pref_name, &value) &&
-      !value->GetListDeprecated().empty()) {
-    return base::ListValue::From(base::Value::ToUniquePtrValue(value->Clone()));
+  const base::Value::List* value =
+      master_dictionary_.FindListByDottedPath(pref_name);
+  if (value && !value->empty()) {
+    return value->Clone();
   }
-  return nullptr;
+  return absl::nullopt;
 }
diff --git a/chrome/browser/profile_resetter/brandcoded_default_settings.h b/chrome/browser/profile_resetter/brandcoded_default_settings.h
index feb2da9..d85b3ac 100644
--- a/chrome/browser/profile_resetter/brandcoded_default_settings.h
+++ b/chrome/browser/profile_resetter/brandcoded_default_settings.h
@@ -30,7 +30,7 @@
   // provided for given setting.
   // After the call return_value contains a list of default engines.
   // |return_value[0]| is default one.
-  std::unique_ptr<base::ListValue> GetSearchProviderOverrides() const;
+  absl::optional<base::Value::List> GetSearchProviderOverrides() const;
 
   bool GetHomepage(std::string* homepage) const;
   absl::optional<bool> GetHomepageIsNewTab() const;
@@ -40,12 +40,12 @@
   bool GetExtensions(std::vector<std::string>* extension_ids) const;
 
   bool GetRestoreOnStartup(int* restore_on_startup) const;
-  std::unique_ptr<base::ListValue> GetUrlsToRestoreOnStartup() const;
+  absl::optional<base::Value::List> GetUrlsToRestoreOnStartup() const;
 
  private:
-  std::unique_ptr<base::ListValue> ExtractList(const char* pref_name) const;
+  absl::optional<base::Value::List> ExtractList(const char* pref_name) const;
 
-  std::unique_ptr<base::DictionaryValue> master_dictionary_;
+  base::Value::Dict master_dictionary_;
 };
 
 #endif  // CHROME_BROWSER_PROFILE_RESETTER_BRANDCODED_DEFAULT_SETTINGS_H_
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index 613c163..0e0aba7 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -48,6 +48,7 @@
 #include "extensions/browser/management_policy.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/manifest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/base_paths.h"
@@ -176,13 +177,13 @@
     DCHECK(prefs);
     TemplateURLPrepopulateData::ClearPrepopulatedEnginesInPrefs(
         profile_->GetPrefs());
-    std::unique_ptr<base::ListValue> search_engines(
+    absl::optional<base::Value::List> search_engines(
         master_settings_->GetSearchProviderOverrides());
-    if (search_engines) {
+    if (search_engines.has_value()) {
       // This Chrome distribution channel provides a custom search engine. We
       // must reset to it.
-      ListPrefUpdate update(prefs, prefs::kSearchProviderOverrides);
-      *update = std::move(*search_engines);
+      prefs->SetList(prefs::kSearchProviderOverrides,
+                     std::move(search_engines).value());
     }
 
     template_url_service_->RepairPrepopulatedSearchEngines();
@@ -298,11 +299,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   PrefService* prefs = profile_->GetPrefs();
   DCHECK(prefs);
-  std::unique_ptr<base::ListValue> url_list(
+  absl::optional<base::Value::List> url_list(
       master_settings_->GetUrlsToRestoreOnStartup());
-  if (url_list) {
-    *ListPrefUpdate(prefs, prefs::kURLsToRestoreOnStartup) =
-        std::move(*url_list);
+  if (url_list.has_value()) {
+    prefs->SetList(prefs::kURLsToRestoreOnStartup, std::move(url_list).value());
   }
 
   int restore_on_startup;
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index bd59d4c..9e70d839 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -786,11 +786,11 @@
   EXPECT_TRUE(settings->GetHomepage(&homepage));
   EXPECT_EQ("http://www.foo.com", homepage);
 
-  std::unique_ptr<base::ListValue> startup_list(
+  absl::optional<base::Value::List> startup_list(
       settings->GetUrlsToRestoreOnStartup());
-  EXPECT_TRUE(startup_list);
+  EXPECT_TRUE(startup_list.has_value());
   std::vector<std::string> startup_pages;
-  for (const auto& entry : startup_list->GetListDeprecated()) {
+  for (const auto& entry : *startup_list) {
     ASSERT_TRUE(entry.is_string());
     startup_pages.push_back(entry.GetString());
   }
diff --git a/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn b/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
index 6be9155..b2c837d 100644
--- a/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
+++ b/chrome/browser/resources/chromeos/cloud_upload/BUILD.gn
@@ -10,10 +10,9 @@
   static_files = [ "main.html" ]
   web_component_files = [ "cloud_upload_dialog.ts" ]
   non_web_component_files = [ "cloud_upload_browser_proxy.ts" ]
-  mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom-webui.js" ]
-  mojo_files_deps = [
-    "//chrome/browser/ui/webui/chromeos/cloud_upload:mojo_bindings_webui_js",
-  ]
+  mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom-webui.js" ]
+  mojo_files_deps =
+      [ "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings_webui_js" ]
   ts_composite = true
 
   html_to_wrapper_template = "native"
diff --git a/chrome/browser/resources/settings/autofill_page/password_view.html b/chrome/browser/resources/settings/autofill_page/password_view.html
index d33bb058..4004c2d 100644
--- a/chrome/browser/resources/settings/autofill_page/password_view.html
+++ b/chrome/browser/resources/settings/autofill_page/password_view.html
@@ -34,14 +34,23 @@
     width: 100%;
   }
 
-  settings-textarea {
-    --cr-input-background-color: transparent;
-    --cr-input-padding-start: 0;
-    --cr-input-readonly-opacity: 1;
+
+  :host-context(.focus-outline-visible) #note:focus,
+  :host-context(.focus-outline-visible) input:focus {
+    outline: 5px auto var(--cr-focus-outline-color);
   }
 
-  settings-textarea.empty-note {
-    --cr-input-color: var(--cr-secondary-text-color);
+  #note {
+    padding-bottom: var(--cr-input-padding-bottom, 6px);
+    padding-inline-start: 0;
+    padding-top: var(--cr-input-padding-top, 6px);
+    white-space: pre-wrap;
+    width: 100%;
+    word-wrap: break-word;
+  }
+
+  #note.empty-note {
+    color: var(--cr-secondary-text-color);
   }
 
   #editButton {
@@ -83,10 +92,12 @@
     </div>
     <template is="dom-if"
         if="[[isNoteEnabled_(credential, isPasswordNotesEnabled_)]]" restamp>
-      <settings-textarea id="note" class$="[[getNoteClass_(credential.note)]]"
-          value="[[getNoteValue_(credential.note)]]" readonly autogrow
-          label="$i18n{passwordNoteLabel}">
-      </settings-textarea>
+      <div id="noteLabel" class="cr-form-field-label">
+        $i18n{passwordNoteLabel}
+      </div>
+      <div id="note" class$="[[getNoteClass_(credential.note)]]"
+          aria-labelledby="noteLabel" role="textbox" aria-readonly="true"
+          tabindex="0">[[getNoteValue_(credential.note)]]</div>
     </template>
   </div>
   <div class="button-container">
diff --git a/chrome/browser/resources/settings/autofill_page/password_view.ts b/chrome/browser/resources/settings/autofill_page/password_view.ts
index d448278..779aeb1 100644
--- a/chrome/browser/resources/settings/autofill_page/password_view.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_view.ts
@@ -10,7 +10,6 @@
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import '../controls/settings_textarea.js';
 import '../i18n_setup.js';
 // <if expr="is_chromeos">
 import '../controls/password_prompt_dialog.js';
@@ -21,8 +20,9 @@
 import './passwords_shared.css.js';
 
 import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
-import {assert} from 'chrome://resources/js/assert_ts.js';
 import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../i18n_setup.js';
@@ -174,6 +174,8 @@
 
     PasswordManagerImpl.getInstance().addPasswordManagerAuthTimeoutListener(
         this.passwordManagerAuthTimeoutListener_);
+
+    FocusOutlineManager.forDocument(document);
   }
 
   override disconnectedCallback() {
diff --git a/chrome/browser/resources/settings/prefs/prefs_mixin.ts b/chrome/browser/resources/settings/prefs/prefs_mixin.ts
index b74b47a1..2614a08 100644
--- a/chrome/browser/resources/settings/prefs/prefs_mixin.ts
+++ b/chrome/browser/resources/settings/prefs/prefs_mixin.ts
@@ -61,6 +61,19 @@
         }
 
         /**
+         * Updates the item in the pref list to the new value. Asserts if the
+         * pref itself is not found or is not an Array type.
+         */
+        updatePrefListItem(key: string, item: any, newItem: any) {
+          const pref = this.getPref(key);
+          assert(pref && pref.type === chrome.settingsPrivate.PrefType.LIST);
+          const index = pref.value.indexOf(item);
+          if (index !== -1) {
+            this.set(`prefs.${key}.value.${index}`, newItem);
+          }
+        }
+
+        /**
          * Deletes the given item from the pref at the given key if the item is
          * found. Asserts if the pref itself is not found or is not an Array
          * type.
@@ -83,5 +96,6 @@
   getPref(prefPath: string): chrome.settingsPrivate.PrefObject;
   setPrefValue(prefPath: string, value: any): void;
   appendPrefListItem(key: string, item: any): void;
+  updatePrefListItem(key: string, item: any, new_item: any): void;
   deletePrefListItem(key: string, item: any): void;
 }
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
index 0c3c5ff..e23f5fca 100644
--- a/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/bookmarks_list.ts
@@ -70,6 +70,7 @@
   private productInfos_: BookmarkProductInfo[];
   hoverVisible: boolean;
   private openFolders_: string[];
+  private shoppingListenerIds_: number[] = [];
 
   override ready() {
     super.ready();
@@ -131,6 +132,12 @@
             'Commerce.PriceTracking.SidePanel.TrackedProductsShown');
       }
     });
+    const callbackRouter = this.shoppingListApi_.getCallbackRouter();
+    this.shoppingListenerIds_.push(
+        callbackRouter.priceTrackedForBookmark.addListener(
+            (product: BookmarkProductInfo) =>
+                this.onBookmarkPriceTracked(product)),
+    );
   }
 
   override disconnectedCallback() {
@@ -138,6 +145,8 @@
       this.bookmarksApi_.callbackRouter[eventName]!.removeListener(callback);
     }
     this.bookmarksDragManager_.stopObserving();
+    this.shoppingListenerIds_.forEach(
+        id => this.shoppingListApi_.getCallbackRouter().removeListener(id));
   }
 
   /** BookmarksDragDelegate */
@@ -413,6 +422,18 @@
       bookmarkElement.focus();
     }
   }
+
+  private onBookmarkPriceTracked(product: BookmarkProductInfo) {
+    // Here we only control the visibility of ShoppingListElement. The same
+    // signal will also be handled in ShoppingListElement to update shopping
+    // list.
+    if (this.productInfos_.length > 0) {
+      return;
+    }
+    this.push('productInfos_', product);
+    chrome.metricsPrivate.recordUserAction(
+        'Commerce.PriceTracking.SidePanel.TrackedProductsShown');
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/side_panel/bookmarks/commerce/shopping_list.ts b/chrome/browser/resources/side_panel/bookmarks/commerce/shopping_list.ts
index 76291d52..45695b60 100644
--- a/chrome/browser/resources/side_panel/bookmarks/commerce/shopping_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/commerce/shopping_list.ts
@@ -43,10 +43,13 @@
 
       untrackedItems_: {
         type: Array,
-        value: [],
+        value: () => [],
       },
 
-      productInfos: Array,
+      productInfos: {
+        type: Array,
+        value: () => [],
+      },
     };
   }
 
@@ -192,6 +195,11 @@
     }
     this.untrackedItems_ = this.untrackedItems_.filter(
         item => item.bookmarkId !== product.bookmarkId);
+    if (!this.isSameProduct_(productItem, product)) {
+      const index = this.productInfos.indexOf(productItem);
+      this.splice('productInfos', index, 1);
+      this.splice('productInfos', index, 0, product);
+    }
   }
 
   private onBookmarkPriceUntracked(bookmarkId: bigint) {
@@ -204,6 +212,18 @@
       this.push('untrackedItems_', untrackedItem);
     }
   }
+
+  private isSameProduct_(
+      itemA: BookmarkProductInfo, itemB: BookmarkProductInfo) {
+    // Only compare the user-visible properties.
+    if (itemA.info.title !== itemB.info.title ||
+        itemA.info.imageUrl.url !== itemB.info.imageUrl.url ||
+        itemA.info.currentPrice !== itemB.info.currentPrice ||
+        itemA.info.previousPrice !== itemB.info.previousPrice) {
+      return false;
+    }
+    return true;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index 0f6b46a..41b6435 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -76,6 +76,8 @@
     "demos/cr_tree/cr_tree_demo.html",
     "demos/cr_toggle_demo.html",
     "demos/md_select/md_select_demo.html",
+    "demos/progress_indicator_nonpolymer_demo.html",
+    "demos/progress_indicator_polymer_demo.html",
     "webui_gallery.html",
   ]
 
diff --git a/chrome/browser/resources/webui_gallery/app.ts b/chrome/browser/resources/webui_gallery/app.ts
index 1c9e4ca7..d52a165ac 100644
--- a/chrome/browser/resources/webui_gallery/app.ts
+++ b/chrome/browser/resources/webui_gallery/app.ts
@@ -65,6 +65,16 @@
               src: 'cr_input/cr_input_demo.html',
             },
             {
+              name: 'Progress indicators, Polymer',
+              path: 'progress-polymer',
+              src: 'progress_indicator_polymer_demo.html',
+            },
+            {
+              name: 'Progress indicators, non-Polymer',
+              path: 'progress-nonpolymer',
+              src: 'progress_indicator_nonpolymer_demo.html',
+            },
+            {
               name: 'Radio buttons and groups',
               path: 'radios',
               src: 'cr_radio_demo.html',
diff --git a/chrome/browser/resources/webui_gallery/demos/progress_indicator_nonpolymer_demo.html b/chrome/browser/resources/webui_gallery/demos/progress_indicator_nonpolymer_demo.html
new file mode 100644
index 0000000..a662fe7
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/progress_indicator_nonpolymer_demo.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Progress indicators demo</title>
+    <link rel="stylesheet" href="chrome://resources/css/spinner.css">
+    <link rel="stylesheet" href="chrome://resources/css/throbber.css">
+    <link rel="stylesheet" href="demo.css">
+  </head>
+  <body>
+    <h1>throbber, 16px</h1>
+    <div class="demos">
+      <div class="throbber"></div>
+    </div>
+
+    <h1>spinner, 22px</h1>
+    <div class="demos">
+      <div class="spinner"></div>
+    </div>
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/demos/progress_indicator_polymer_demo.html b/chrome/browser/resources/webui_gallery/demos/progress_indicator_polymer_demo.html
new file mode 100644
index 0000000..1de9ef2
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/progress_indicator_polymer_demo.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Progress indicators demo</title>
+    <link rel="stylesheet" href="demo.css">
+    <style>
+      paper-progress {
+        --paper-progress-active-color: var(--google-blue-500);
+      }
+
+      @media (prefers-color-scheme: dark) {
+        paper-progress {
+          --paper-progress-active-color: var(--google-blue-300);
+        }
+      }
+
+      paper-spinner-lite {
+        --paper-spinner-color: var(--google-blue-500);
+      }
+
+      @media (prefers-color-scheme: dark) {
+        paper-spinner-lite {
+          --paper-spinner-color: var(--google-blue-300);
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <h1>paper-progress</h1>
+    <div class="demos">
+      <paper-progress indeterminate></paper-progress>
+    </div>
+
+    <h1>paper-spinner-lite</h1>
+    <div class="demos">
+      <paper-spinner-lite active></paper-spinner-lite>
+    </div>
+
+    <script src="chrome://resources/cr_elements/cr_shared_vars.css.js"
+        type="module"></script>
+    <script
+        src="chrome://resources/polymer/v3_0/paper-progress/paper-progress.js"
+        type="module"></script>
+    <script
+        src="chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js"
+        type="module"></script>
+  </body>
+</html>
diff --git a/chrome/browser/resources/welcome/shared/splash_pages_shared.css b/chrome/browser/resources/welcome/shared/splash_pages_shared.css
index bf2cc59..56d0605 100644
--- a/chrome/browser/resources/welcome/shared/splash_pages_shared.css
+++ b/chrome/browser/resources/welcome/shared/splash_pages_shared.css
@@ -27,6 +27,8 @@
   display: flex;
   flex-basis: 45vh;
   flex-direction: column;
+  /* Put the text above the onboarding-background. */
+  z-index: 1;
 }
 
 .header {
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 d5e4d1d..f611c170 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
@@ -49,10 +49,6 @@
 // Content type of the metadata and file contents.
 const char kDataContentType[] = "Content-Type: application/octet-stream";
 
-void RecordUploadSuccessHistogram(bool success) {
-  base::UmaHistogramBoolean("SBMultipartUploader.UploadSuccess", success);
-}
-
 std::unique_ptr<MultipartDataPipeGetter> CreateFileDataPipeGetterBlocking(
     const std::string& boundary,
     const std::string& metadata,
@@ -195,8 +191,6 @@
       network::SimpleURLLoader::Create(std::move(request), traffic_annotation_);
   url_loader_->SetAllowHttpErrorResults(true);
   std::string request_body = GenerateRequestBody(metadata_, data_);
-  base::UmaHistogramMemoryKB("SBMultipartUploader.UploadSize",
-                             request_body.size());
   url_loader_->AttachStringForUpload(request_body,
                                      kUploadContentType + boundary_);
   url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
@@ -287,24 +281,11 @@
     int net_error,
     int response_code,
     std::unique_ptr<std::string> response_body) {
-  base::UmaHistogramSparse(
-      "SBMultipartUploader.NetworkRequestResponseCodeOrError",
-      net_error == net::OK ? response_code : net_error);
-
   if (net_error == net::OK && response_code == net::HTTP_OK) {
-    RecordUploadSuccessHistogram(/*success=*/true);
-    base::UmaHistogramExactLinear("SBMultipartUploader.RetriesNeeded",
-                                  retry_count_, kMaxRetryAttempts);
-    base::UmaHistogramMediumTimes(
-        "SBMultipartUploader.SuccessfulUploadDuration",
-        base::Time::Now() - start_time_);
     std::move(callback_).Run(/*success=*/true, response_code,
                              *response_body.get());
   } else {
     if (response_code < 500 || retry_count_ >= kMaxRetryAttempts) {
-      RecordUploadSuccessHistogram(/*success=*/false);
-      base::UmaHistogramMediumTimes("SBMultipartUploader.FailedUploadDuration",
-                                    base::Time::Now() - start_time_);
       std::move(callback_).Run(/*success=*/false, response_code,
                                *response_body.get());
     } else {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader_unittest.cc
index 56a6aaa7..1d1026d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader_unittest.cc
@@ -182,127 +182,6 @@
   }
 }
 
-TEST_F(MultipartUploadRequestTest,
-       EmitsNetworkRequestResponseCodeOrErrorHistogram) {
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_OK,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample(
-        "SBMultipartUploader.NetworkRequestResponseCodeOrError", net::HTTP_OK,
-        1);
-  }
-
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_FORBIDDEN,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample(
-        "SBMultipartUploader.NetworkRequestResponseCodeOrError",
-        net::HTTP_FORBIDDEN, 1);
-  }
-
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::ERR_FAILED, net::HTTP_OK,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample(
-        "SBMultipartUploader.NetworkRequestResponseCodeOrError",
-        net::ERR_FAILED, 1);
-  }
-
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample(
-        "SBMultipartUploader.NetworkRequestResponseCodeOrError",
-        net::HTTP_INTERNAL_SERVER_ERROR, 3);
-  }
-}
-
-TEST_F(MultipartUploadRequestTest, EmitsUploadSuccessHistogram) {
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_OK,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample("SBMultipartUploader.UploadSuccess", true, 1);
-  }
-
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_FORBIDDEN,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample("SBMultipartUploader.UploadSuccess", false,
-                                  1);
-  }
-}
-
-TEST_F(MultipartUploadRequestTest, EmitsRetriesNeededHistogram) {
-  {
-    base::HistogramTester histograms;
-    MockMultipartUploadRequest mock_request;
-
-    EXPECT_CALL(mock_request, SendRequest())
-        .WillRepeatedly(Invoke([&mock_request]() {
-          mock_request.RetryOrFinish(net::OK, net::HTTP_OK,
-                                     std::make_unique<std::string>("response"));
-        }));
-    mock_request.Start();
-    task_environment_.FastForwardUntilNoTasksRemain();
-
-    histograms.ExpectUniqueSample("SBMultipartUploader.RetriesNeeded", 0, 1);
-  }
-}
-
 class MultipartUploadDataPipeRequestTest
     : public MultipartUploadRequestTest,
       public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
index 3c3c702..f10e985 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -472,6 +472,8 @@
 
   for (const std::string& param : param_values) {
     auto config = ParseConfigFromString(param);
+    VLOG(1) << "Segmentation config param from experiment, "
+            << (config ? "added successfully: " : "failed to parse: ") << param;
     if (config) {
       out_configs.push_back(std::move(config));
     }
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 7b43693c..fc5b062 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -119,8 +119,9 @@
     // preferences for non-Hunspell languages so that there is no attempt to
     // load a non-existent Hunspell dictionary, and so that Hunspell
     // spellchecking isn't broken because of the failed load.
-    ListPrefUpdate update(prefs, spellcheck::prefs::kSpellCheckDictionaries);
-    update->GetList().EraseIf([](const base::Value& entry) {
+    ScopedListPrefUpdate update(prefs,
+                                spellcheck::prefs::kSpellCheckDictionaries);
+    update->EraseIf([](const base::Value& entry) {
       return spellcheck::GetCorrespondingSpellCheckLanguage(entry.GetString())
           .empty();
     });
@@ -653,16 +654,17 @@
   DCHECK(prefs);
   // When following object goes out of scope, preference change observers will
   // be notified (even if there is no preference change).
-  ListPrefUpdate update(prefs, spellcheck::prefs::kSpellCheckDictionaries);
-  update->GetList().EraseIf([this](const base::Value& entry) {
+  ScopedListPrefUpdate update(prefs,
+                              spellcheck::prefs::kSpellCheckDictionaries);
+  update->EraseIf([this](const base::Value& entry) {
     const std::string dictionary_name = entry.GetString();
     return (!UsesWindowsDictionary(dictionary_name) &&
             spellcheck::GetCorrespondingSpellCheckLanguage(dictionary_name)
                 .empty());
   });
 
-  // No need to call LoadDictionaries() as when the ListPrefUpdate object goes
-  // out of scope, the preference change handler will do this.
+  // No need to call LoadDictionaries() as when the ScopedListPrefUpdate object
+  // goes out of scope, the preference change handler will do this.
 }
 
 bool SpellcheckService::UsesWindowsDictionary(
diff --git a/chrome/browser/supervised_user/web_approvals_manager.cc b/chrome/browser/supervised_user/web_approvals_manager.cc
index 1b0ffa62..7ad4a7bb 100644
--- a/chrome/browser/supervised_user/web_approvals_manager.cc
+++ b/chrome/browser/supervised_user/web_approvals_manager.cc
@@ -47,17 +47,20 @@
     const GURL& url,
     ApprovalRequestInitiatedCallback callback) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  // TODO(crbug.com/1233615): pass completion_callback.
-  // TODO(b/195316776): Pass in relevant local web approvals params instead of
-  // an empty object.
+  // TODO(b/195316776): replace this with call to the ParentAccess crosapi with
+  // appropriate parameters and handle the ParentAccess crosapi result.
   parent_access_ui::mojom::ParentAccessParamsPtr params =
       parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
               parent_access_ui::mojom::WebApprovalsParams::New()));
   chromeos::ParentAccessDialog::ShowError result =
-      chromeos::ParentAccessDialog::Show(std::move(params));
-
+      chromeos::ParentAccessDialog::Show(
+          std::move(params),
+          base::BindOnce(
+              [](std::unique_ptr<
+                  chromeos::ParentAccessDialog::ParentAccessDialog::Result>
+                     result) -> void {}));
   if (result != chromeos::ParentAccessDialog::ShowError::kNone) {
     LOG(ERROR) << "Error showing ParentAccessDialog: " << result;
     std::move(callback).Run(false);
diff --git a/chrome/browser/sync/test/integration/printers_helper.cc b/chrome/browser/sync/test/integration/printers_helper.cc
index 0b3cf30..28576e8 100644
--- a/chrome/browser/sync/test/integration/printers_helper.cc
+++ b/chrome/browser/sync/test/integration/printers_helper.cc
@@ -47,8 +47,9 @@
 
     auto it = std::find_if(
         begin, end,
-        [&a](const std::pair<std::string, const chromeos::Printer*>& entry)
-            -> bool { return PrintersAreMostlyEqual(a, *(entry.second)); });
+        [&a](const std::pair<std::string, const chromeos::Printer*>& entry) {
+          return PrintersAreMostlyEqual(a, *(entry.second));
+        });
 
     if (it == end) {
       // Element in a does not match an element in b. Lists do not contain the
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index ce72f5a..b281f2a2 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2597,6 +2597,20 @@
       "webui/ash/cellular_setup/mobile_setup_dialog.h",
       "webui/ash/cellular_setup/mobile_setup_ui.cc",
       "webui/ash/cellular_setup/mobile_setup_ui.h",
+      "webui/ash/cloud_upload/cloud_upload_dialog.cc",
+      "webui/ash/cloud_upload/cloud_upload_dialog.h",
+      "webui/ash/cloud_upload/cloud_upload_notification_manager.cc",
+      "webui/ash/cloud_upload/cloud_upload_notification_manager.h",
+      "webui/ash/cloud_upload/cloud_upload_page_handler.cc",
+      "webui/ash/cloud_upload/cloud_upload_page_handler.h",
+      "webui/ash/cloud_upload/cloud_upload_ui.cc",
+      "webui/ash/cloud_upload/cloud_upload_ui.h",
+      "webui/ash/cloud_upload/cloud_upload_util.cc",
+      "webui/ash/cloud_upload/cloud_upload_util.h",
+      "webui/ash/cloud_upload/drive_upload_handler.cc",
+      "webui/ash/cloud_upload/drive_upload_handler.h",
+      "webui/ash/cloud_upload/one_drive_upload_handler.cc",
+      "webui/ash/cloud_upload/one_drive_upload_handler.h",
       "webui/chromeos/assistant_optin/assistant_optin_ui.cc",
       "webui/chromeos/assistant_optin/assistant_optin_ui.h",
       "webui/chromeos/assistant_optin/assistant_optin_utils.cc",
@@ -2607,20 +2621,6 @@
       "webui/chromeos/bluetooth_shared_load_time_data_provider.h",
       "webui/chromeos/certificate_manager_dialog_ui.cc",
       "webui/chromeos/certificate_manager_dialog_ui.h",
-      "webui/chromeos/cloud_upload/cloud_upload_dialog.cc",
-      "webui/chromeos/cloud_upload/cloud_upload_dialog.h",
-      "webui/chromeos/cloud_upload/cloud_upload_notification_manager.cc",
-      "webui/chromeos/cloud_upload/cloud_upload_notification_manager.h",
-      "webui/chromeos/cloud_upload/cloud_upload_page_handler.cc",
-      "webui/chromeos/cloud_upload/cloud_upload_page_handler.h",
-      "webui/chromeos/cloud_upload/cloud_upload_ui.cc",
-      "webui/chromeos/cloud_upload/cloud_upload_ui.h",
-      "webui/chromeos/cloud_upload/cloud_upload_util.cc",
-      "webui/chromeos/cloud_upload/cloud_upload_util.h",
-      "webui/chromeos/cloud_upload/drive_upload_handler.cc",
-      "webui/chromeos/cloud_upload/drive_upload_handler.h",
-      "webui/chromeos/cloud_upload/one_drive_upload_handler.cc",
-      "webui/chromeos/cloud_upload/one_drive_upload_handler.h",
       "webui/chromeos/connectivity_diagnostics_dialog.cc",
       "webui/chromeos/connectivity_diagnostics_dialog.h",
       "webui/chromeos/crostini_installer/crostini_installer_dialog.cc",
@@ -3180,7 +3180,7 @@
       "//chrome/browser/ui/quick_answers",
       "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
       "//chrome/browser/ui/webui/ash/audio:mojo_bindings",
-      "//chrome/browser/ui/webui/chromeos/cloud_upload:mojo_bindings",
+      "//chrome/browser/ui/webui/ash/cloud_upload:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/crostini_upgrader:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/launcher_internals:mojo_bindings",
       "//chrome/browser/ui/webui/chromeos/manage_mirrorsync:mojo_bindings",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 86a4046..d1aefa7f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3205,6 +3205,7 @@
       <message name="IDS_NTP_FEED_MENU_IPH" desc="In-product help that points at the menu icon for the news feed on Chrome's new tab page. This string instructs the user to open the menu for settings that let them control the content that appears on the feed.">
         Control your stories and activity here
       </message>
+      <message name="IDS_NTP_NEW" desc="Text in a badge indicating a section is new, thereby drawing attention to a new feature. [CHAR_LIMIT=6]">New</message>
       <message name="IDS_ACCESSIBILITY_NTP_FEED_MENU_BUTTON" desc="Content description for the feed header menu button. Please use the branded term for Discover, as listed under Product Names in the Google Glossary Manager (TC ID 1799975766543019278).">
         Options for Discover
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NTP_NEW.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NTP_NEW.png.sha1
new file mode 100644
index 0000000..b594b36
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_NTP_NEW.png.sha1
@@ -0,0 +1 @@
+734e387d29a4f80dd0f1ebc86c23f865fdeb9bdf
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index bceff683..df1f6d19 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -753,24 +753,22 @@
   GetInt64FromPref(package, kLastBackupTime, &last_backup_time);
   const base::Value* permission_val = package->Find(kPermissionStates);
   if (permission_val) {
-    const base::DictionaryValue* permission_dict = nullptr;
-    permission_val->GetAsDictionary(&permission_dict);
+    const base::Value::Dict* permission_dict = permission_val->GetIfDict();
     DCHECK(permission_dict);
 
-    for (const auto item : permission_dict->GetDict()) {
+    for (const auto iter : *permission_dict) {
       int64_t permission_type = -1;
-      base::StringToInt64(item.first, &permission_type);
+      base::StringToInt64(iter.first, &permission_type);
       DCHECK_NE(-1, permission_type);
 
-      const base::Value& permission_state = item.second;
+      const base::Value& permission_state = iter.second;
 
-      const base::DictionaryValue* permission_state_dict;
-      if (permission_state.GetAsDictionary(&permission_state_dict)) {
-        bool granted = permission_state_dict->GetDict()
-                           .FindBool(kPermissionStateGranted)
+      const base::Value::Dict* permission_state_dict =
+          permission_state.GetIfDict();
+      if (permission_state_dict) {
+        bool granted = permission_state_dict->FindBool(kPermissionStateGranted)
                            .value_or(false);
-        bool managed = permission_state_dict->GetDict()
-                           .FindBool(kPermissionStateManaged)
+        bool managed = permission_state_dict->FindBool(kPermissionStateManaged)
                            .value_or(false);
         arc::mojom::AppPermission permission =
             static_cast<arc::mojom::AppPermission>(permission_type);
diff --git a/chrome/browser/ui/ash/OWNERS b/chrome/browser/ui/ash/OWNERS
index dca5d428..e20cf67 100644
--- a/chrome/browser/ui/ash/OWNERS
+++ b/chrome/browser/ui/ash/OWNERS
@@ -15,6 +15,7 @@
 per-file *capture_mode*=file://ash/capture_mode/OWNERS
 per-file recording_service_browsertest.cc=file://ash/capture_mode/OWNERS
 per-file thumbnail_loader*=file://chrome/browser/ui/ash/holding_space/OWNERS
+per-file system_tray_tray_cast_browsertest_media_router_chromeos.cc=file://chrome/browser/media/router/discovery/access_code/OWNERS
 
 # Must be updated when a new OS settings section is added.
 per-file chrome_new_window_client*=file://chrome/browser/resources/settings/chromeos/OWNERS
diff --git a/chrome/browser/ui/ash/login_screen_client_impl.cc b/chrome/browser/ui/ash/login_screen_client_impl.cc
index aace37c..398f28c 100644
--- a/chrome/browser/ui/ash/login_screen_client_impl.cc
+++ b/chrome/browser/ui/ash/login_screen_client_impl.cc
@@ -351,7 +351,7 @@
 
 void LoginScreenClientImpl::OnMaxIncorrectPasswordAttempted(
     const AccountId& account_id) {
-  RecordReauthReason(account_id, ash::ReauthReason::INCORRECT_PASSWORD_ENTERED);
+  RecordReauthReason(account_id, ash::ReauthReason::kIncorrectPasswordEntered);
 }
 
 void LoginScreenClientImpl::SetPublicSessionKeyboardLayout(
diff --git a/chrome/browser/ui/ash/network/network_portal_notification_controller.cc b/chrome/browser/ui/ash/network/network_portal_notification_controller.cc
index 9863a6d..ed855dd 100644
--- a/chrome/browser/ui/ash/network/network_portal_notification_controller.cc
+++ b/chrome/browser/ui/ash/network/network_portal_notification_controller.cc
@@ -172,6 +172,7 @@
        portal_state != NetworkState::PortalState::kPortalSuspected &&
        portal_state != NetworkState::PortalState::kProxyAuthRequired)) {
     last_network_guid_.clear();
+    last_portal_state_ = portal_state;
 
     // In browser tests we initiate fake network portal detection, but network
     // state usually stays connected. This way, after dialog is shown, it is
@@ -191,10 +192,13 @@
     return;
 
   // Don't do anything if notification for |network| already was
-  // displayed.
-  if (network->guid() == last_network_guid_)
-    return;
+  // displayed with the same portal_state.
+  if (network->guid() == last_network_guid_ &&
+      portal_state == last_portal_state_) {
+        return;
+  }
   last_network_guid_ = network->guid();
+  last_portal_state_ = portal_state;
 
   std::unique_ptr<message_center::Notification> notification =
       CreateDefaultCaptivePortalNotification(network, portal_state);
diff --git a/chrome/browser/ui/ash/network/network_portal_notification_controller.h b/chrome/browser/ui/ash/network/network_portal_notification_controller.h
index ea2d9c2..4c61184 100644
--- a/chrome/browser/ui/ash/network/network_portal_notification_controller.h
+++ b/chrome/browser/ui/ash/network/network_portal_notification_controller.h
@@ -76,6 +76,10 @@
   // signin UI.
   std::unique_ptr<NetworkPortalSigninController> signin_controller_;
 
+  // Last portal state for which notification was displayed.
+  NetworkState::PortalState last_portal_state_ =
+      NetworkState::PortalState::kUnknown;
+
   // Do not close Portal Login dialog on "No network" error in browser tests.
   bool ignore_no_network_for_testing_ = false;
 };
diff --git a/chrome/browser/ui/ash/shelf/browser_app_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/browser_app_shelf_item_controller.cc
index ddd907b8..7372a405 100644
--- a/chrome/browser/ui/ash/shelf/browser_app_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/browser_app_shelf_item_controller.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
+#include "chromeos/ui/wm/desks/desks_helper.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -21,6 +22,31 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/views/widget/native_widget_aura.h"
 
+namespace {
+
+// Returns true if we are trying to launch a lacros window if there isn't
+// already one on the active desk.
+bool ShouldLaunchNewLacrosWindow(
+    std::string app_id,
+    const std::vector<std::pair<int, base::UnguessableToken>>& instances,
+    const apps::BrowserAppInstanceRegistry& registry) {
+  if (app_id != app_constants::kLacrosAppId) {
+    return false;
+  }
+
+  for (auto [cmd_id, instance_id] : instances) {
+    aura::Window* window = registry.GetWindowByInstanceId(instance_id);
+    if (window &&
+        chromeos::DesksHelper::Get(window)->BelongsToActiveDesk(window)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
 BrowserAppShelfItemController::BrowserAppShelfItemController(
     const ash::ShelfID& shelf_id,
     Profile* profile)
@@ -46,36 +72,33 @@
     ItemSelectedCallback callback,
     const ItemFilterPredicate& filter_predicate) {
   auto instances = GetMatchingInstances(filter_predicate);
-  switch (instances.size()) {
-    case 0:
-      // Nothing is running, launch.
-      std::move(callback).Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, {});
-      ChromeShelfController::instance()->LaunchApp(
-          ash::ShelfID(shelf_id()), source, ui::EF_NONE, display_id);
-      break;
-    case 1: {
-      // One instance is running, activate it.
-      base::UnguessableToken id = instances[0].second;
-      const bool can_minimize = source != ash::LAUNCH_FROM_APP_LIST &&
-                                source != ash::LAUNCH_FROM_APP_LIST_SEARCH;
-      ash::ShelfAction action;
-      if (registry_.IsInstanceActive(id) && can_minimize) {
-        registry_.MinimizeInstance(id);
-        action = ash::SHELF_ACTION_WINDOW_MINIMIZED;
-      } else {
-        registry_.ActivateInstance(id);
-        action = ash::SHELF_ACTION_WINDOW_ACTIVATED;
-      }
-      std::move(callback).Run(action, {});
-      break;
+  if (instances.size() == 0 ||
+      ShouldLaunchNewLacrosWindow(app_id(), instances, registry_)) {
+    // No instances or if this is a lacros window and there isn't already one on
+    // the current workspace, launch.
+    std::move(callback).Run(ash::SHELF_ACTION_NEW_WINDOW_CREATED, {});
+    ChromeShelfController::instance()->LaunchApp(
+        ash::ShelfID(shelf_id()), source, ui::EF_NONE, display_id);
+  } else if (instances.size() == 1) {
+    // One instance is running, activate it.
+    const base::UnguessableToken id = instances[0].second;
+    const bool can_minimize = source != ash::LAUNCH_FROM_APP_LIST &&
+                              source != ash::LAUNCH_FROM_APP_LIST_SEARCH;
+    ash::ShelfAction action;
+    if (registry_.IsInstanceActive(id) && can_minimize) {
+      registry_.MinimizeInstance(id);
+      action = ash::SHELF_ACTION_WINDOW_MINIMIZED;
+    } else {
+      registry_.ActivateInstance(id);
+      action = ash::SHELF_ACTION_WINDOW_ACTIVATED;
     }
-    default:
-      // Multiple instances activated, show the list of running instances.
-      std::move(callback).Run(
-          ash::SHELF_ACTION_NONE,
-          GetAppMenuItems(event ? event->flags() : ui::EF_NONE,
-                          filter_predicate));
-      break;
+    std::move(callback).Run(action, {});
+  } else {
+    // Multiple instances activated, show the list of running instances.
+    std::move(callback).Run(
+        ash::SHELF_ACTION_NONE,
+        GetAppMenuItems(event ? event->flags() : ui::EF_NONE,
+                        filter_predicate));
   }
 }
 
diff --git a/chrome/browser/ui/media_router/media_router_ui.cc b/chrome/browser/ui/media_router/media_router_ui.cc
index bedeba7..8f56433 100644
--- a/chrome/browser/ui/media_router/media_router_ui.cc
+++ b/chrome/browser/ui/media_router/media_router_ui.cc
@@ -211,7 +211,7 @@
     return false;
   }
 
-  GetIssueManager()->ClearNonBlockingIssues();
+  GetIssueManager()->ClearAllIssues();
 
   current_route_request_ = absl::make_optional(*params->request);
 
diff --git a/chrome/browser/ui/media_router/media_router_ui_unittest.cc b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
index 066bac2e..c4a8789 100644
--- a/chrome/browser/ui/media_router/media_router_ui_unittest.cc
+++ b/chrome/browser/ui/media_router/media_router_ui_unittest.cc
@@ -510,7 +510,7 @@
   }));
   ui_->SendIssueForRouteTimeout(MediaCastMode::PRESENTATION, "sink_id",
                                 u"presentation_source_name");
-  mock_router_->GetIssueManager()->ClearNonBlockingIssues();
+  mock_router_->GetIssueManager()->ClearAllIssues();
 
   EXPECT_CALL(issues_observer, OnIssue).WillOnce(Invoke([](const Issue& issue) {
     EXPECT_EQ(l10n_util::GetStringUTF8(
@@ -518,7 +518,7 @@
               issue.info().title);
   }));
   ui_->SendIssueForRouteTimeout(MediaCastMode::TAB_MIRROR, "sink_id", u"");
-  mock_router_->GetIssueManager()->ClearNonBlockingIssues();
+  mock_router_->GetIssueManager()->ClearAllIssues();
 
   EXPECT_CALL(issues_observer, OnIssue).WillOnce(Invoke([](const Issue& issue) {
     EXPECT_EQ(l10n_util::GetStringUTF8(
@@ -526,7 +526,7 @@
               issue.info().title);
   }));
   ui_->SendIssueForRouteTimeout(MediaCastMode::DESKTOP_MIRROR, "sink_id", u"");
-  mock_router_->GetIssueManager()->ClearNonBlockingIssues();
+  mock_router_->GetIssueManager()->ClearAllIssues();
 
   EXPECT_CALL(issues_observer, OnIssue).WillOnce(Invoke([](const Issue& issue) {
     EXPECT_EQ(
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index 24aa152..0a4b832 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -195,8 +195,8 @@
   friend class TabStripTestBase;
   FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabCloseButtonVisibility);
   FRIEND_TEST_ALL_PREFIXES(TabTest, TitleTextHasSufficientContrast);
-  FRIEND_TEST_ALL_PREFIXES(TabHoverCardBubbleViewBrowserTest,
-                           WidgetVisibleOnTabCloseButtonFocusAfterTabFocus);
+  FRIEND_TEST_ALL_PREFIXES(TabHoverCardInteractiveUiTest,
+                           HoverCardVisibleOnTabCloseButtonFocusAfterTabFocus);
 
   // Invoked from Layout to adjust the position of the favicon or alert
   // indicator for pinned tabs. The visual_width parameter is how wide the
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index e66d7ad..dfc03d69 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -847,7 +847,8 @@
   // Note that this code has to go after CreateBubble() above, since setting up
   // the placeholder image and background color require a ColorProvider, which
   // is only available once this View has been added to its widget.
-  if (thumbnail_view_ && !tab->data().thumbnail->has_data() &&
+  if (thumbnail_view_ &&
+      (!tab->data().thumbnail || !tab->data().thumbnail->has_data()) &&
       !tab->IsActive()) {
     thumbnail_view_->SetPlaceholderImage();
   }
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
deleted file mode 100644
index 19fb835..0000000
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/memory/raw_ptr.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_command_controller.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/test/test_browser_dialog.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/tabs/tab.h"
-#include "chrome/browser/ui/views/tabs/tab_close_button.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_metrics.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/reputation/core/safety_tip_test_utils.h"
-#include "content/public/test/browser_test.h"
-#include "net/base/url_util.h"
-#include "net/dns/mock_host_resolver.h"
-#include "ui/gfx/animation/animation_test_api.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/test/widget_test.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
-
-using views::Widget;
-
-class TabHoverCardBubbleViewBrowserTest : public DialogBrowserTest {
- public:
-  TabHoverCardBubbleViewBrowserTest()
-      : animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
-            gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) {
-    TabHoverCardController::disable_animations_for_testing_ = true;
-  }
-  TabHoverCardBubbleViewBrowserTest(const TabHoverCardBubbleViewBrowserTest&) =
-      delete;
-  TabHoverCardBubbleViewBrowserTest& operator=(
-      const TabHoverCardBubbleViewBrowserTest&) = delete;
-  ~TabHoverCardBubbleViewBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    DialogBrowserTest::SetUpOnMainThread();
-    tab_strip_ = BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
-  }
-
-  TabStrip* tab_strip() { return tab_strip_; }
-
-  TabHoverCardBubbleView* hover_card() {
-    return tab_strip()->hover_card_controller_->hover_card_;
-  }
-
-  std::u16string GetHoverCardTitle() {
-    return hover_card()->GetTitleTextForTesting();
-  }
-
-  std::u16string GetHoverCardDomain() {
-    return hover_card()->GetDomainTextForTesting();
-  }
-
-  int GetHoverCardsSeenCount() {
-    return tab_strip()
-        ->hover_card_controller_->metrics_for_testing()
-        ->cards_seen_count();
-  }
-
-  void MouseExitTabStrip() {
-    ui::MouseEvent stop_hover_event(ui::ET_MOUSE_EXITED, gfx::Point(),
-                                    gfx::Point(), base::TimeTicks(),
-                                    ui::EF_NONE, 0);
-    tab_strip()->OnMouseExited(stop_hover_event);
-  }
-
-  void ClickMouseOnTab(int index) {
-    Tab* const tab = tab_strip()->tab_at(index);
-    ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                               base::TimeTicks(), ui::EF_NONE, 0);
-    tab->OnMousePressed(click_event);
-  }
-
-  void HoverMouseOverTabAt(int index) {
-    // We don't use Tab::OnMouseEntered here to invoke the hover card because
-    // that path is disabled in browser tests. If we enabled it, the real mouse
-    // might interfere with the test.
-    tab_strip()->UpdateHoverCard(
-        tab_strip()->tab_at(index),
-        TabSlotController::HoverCardUpdateType::kHover);
-  }
-
-  // DialogBrowserTest:
-  void ShowUi(const std::string& name) override {
-    HoverMouseOverTabAt(0);
-    views::test::WidgetVisibleWaiter(hover_card()->GetWidget()).Wait();
-  }
-
- private:
-  std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>>
-      animation_mode_reset_;
-
-  raw_ptr<TabStrip> tab_strip_ = nullptr;
-};
-
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       InvokeUi_tab_hover_card) {
-  ShowAndVerifyUi();
-}
-
-// Verify hover card is visible while hovering and not visible outside of the
-// tabstrip.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_WidgetVisibleOnHover DISABLED_WidgetVisibleOnHover
-#else
-#define MAYBE_WidgetVisibleOnHover WidgetVisibleOnHover
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetVisibleOnHover) {
-  ShowUi("default");
-  Widget* const widget = hover_card()->GetWidget();
-
-  ASSERT_NE(nullptr, widget);
-  EXPECT_TRUE(widget->IsVisible());
-  MouseExitTabStrip();
-  EXPECT_FALSE(widget->IsVisible());
-}
-
-// Verify hover card is visible when tab is focused.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_WidgetVisibleOnTabFocus DISABLED_WidgetVisibleOnTabFocus
-#else
-#define MAYBE_WidgetVisibleOnTabFocus WidgetVisibleOnTabFocus
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetVisibleOnTabFocus) {
-  Tab* const tab = tab_strip()->tab_at(0);
-  tab_strip()->GetFocusManager()->SetFocusedView(tab);
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-}
-
-// Verify hover card is visible when focus moves from the tab to tab close
-// button.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_WidgetVisibleOnTabCloseButtonFocusAfterTabFocus \
-  DISABLED_WidgetVisibleOnTabCloseButtonFocusAfterTabFocus
-#else
-#define MAYBE_WidgetVisibleOnTabCloseButtonFocusAfterTabFocus \
-  WidgetVisibleOnTabCloseButtonFocusAfterTabFocus
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetVisibleOnTabCloseButtonFocusAfterTabFocus) {
-  Tab* const tab = tab_strip()->tab_at(0);
-  tab_strip()->GetFocusManager()->SetFocusedView(tab);
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-  tab_strip()->GetFocusManager()->SetFocusedView(tab->close_button_);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-}
-
-// Verify hover card is visible when tab is focused and a key is pressed.
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       WidgetVisibleOnKeyPressAfterTabFocus) {
-  Tab* const tab = tab_strip()->tab_at(0);
-  tab_strip()->GetFocusManager()->SetFocusedView(tab);
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-
-  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0);
-  tab->OnKeyPressed(key_event);
-  EXPECT_TRUE(widget->IsVisible());
-}
-
-// Verify hover card is not visible when tab is focused and the mouse is
-// pressed.
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       WidgetNotVisibleOnMousePressAfterTabFocus) {
-  Tab* const tab = tab_strip()->tab_at(0);
-  tab_strip()->GetFocusManager()->SetFocusedView(tab);
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-
-  ClickMouseOnTab(0);
-  EXPECT_FALSE(widget->IsVisible());
-}
-
-// Verify hover card is visible after navigating to the tab strip using keyboard
-// accelerators.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_WidgetVisibleOnTabFocusFromKeyboardAccelerator \
-  DISABLED_WidgetVisibleOnTabFocusFromKeyboardAccelerator
-#else
-#define MAYBE_WidgetVisibleOnTabFocusFromKeyboardAccelerator \
-  WidgetVisibleOnTabFocusFromKeyboardAccelerator
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetVisibleOnTabFocusFromKeyboardAccelerator) {
-  TabRendererData new_tab_data = TabRendererData();
-  new_tab_data.title = u"Test Tab 2";
-  new_tab_data.last_committed_url =
-      GURL("http://example.com/this/should/not/be/seen");
-  tab_strip()->AddTabAt(1, new_tab_data);
-
-  // Cycle focus until it reaches a tab.
-  while (!tab_strip()->IsFocusInTabs())
-    browser()->command_controller()->ExecuteCommand(IDC_FOCUS_NEXT_PANE);
-
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-
-  // Move focus forward to the close button or next tab dependent on window
-  // size.
-  tab_strip()->AcceleratorPressed(ui::Accelerator(ui::VKEY_RIGHT, ui::EF_NONE));
-  EXPECT_TRUE(widget->IsVisible());
-}
-
-// Verify hover card is not visible after clicking on a tab.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_WidgetNotVisibleOnClick DISABLED_WidgetNotVisibleOnClick
-#else
-#define MAYBE_WidgetNotVisibleOnClick WidgetNotVisibleOnClick
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetNotVisibleOnClick) {
-  ShowUi("default");
-  Widget* const widget = hover_card()->GetWidget();
-
-  ASSERT_NE(nullptr, widget);
-  EXPECT_TRUE(widget->IsVisible());
-  ClickMouseOnTab(0);
-  EXPECT_FALSE(widget->IsVisible());
-}
-
-// Verify title, domain, and anchor are correctly updated when moving hover
-// from one tab to another.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_WidgetDataUpdate DISABLED_WidgetDataUpdate
-#else
-#define MAYBE_WidgetDataUpdate WidgetDataUpdate
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_WidgetDataUpdate) {
-  TabRendererData new_tab_data = TabRendererData();
-  new_tab_data.title = u"Test Tab 2";
-  new_tab_data.last_committed_url =
-      GURL("http://example.com/this/should/not/be/seen");
-  tab_strip()->AddTabAt(1, new_tab_data);
-
-  ShowUi("default");
-
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  EXPECT_TRUE(widget->IsVisible());
-  HoverMouseOverTabAt(1);
-  ASSERT_NE(nullptr, widget);
-  EXPECT_TRUE(widget->IsVisible());
-  Tab* const tab = tab_strip()->tab_at(1);
-  EXPECT_EQ(GetHoverCardTitle(), u"Test Tab 2");
-  EXPECT_EQ(GetHoverCardDomain(), u"example.com");
-  EXPECT_EQ(hover_card()->GetAnchorView(), static_cast<views::View*>(tab));
-}
-
-// Verify inactive window remains inactive when showing a hover card for a tab
-// in the inactive window.
-// TODO(crbug.com/1050765): the test is flaky.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_InactiveWindowStaysInactiveOnHover \
-  DISABLED_InactiveWindowStaysInactiveOnHover
-#else
-#define MAYBE_InactiveWindowStaysInactiveOnHover \
-  InactiveWindowStaysInactiveOnHover
-#endif
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       MAYBE_InactiveWindowStaysInactiveOnHover) {
-  const BrowserList* active_browser_list_ = BrowserList::GetInstance();
-  // Open a second window. On Windows, Widget::Deactivate() works by activating
-  // the next topmost window on the z-order stack. So instead we use two windows
-  // and Widget::Activate() here to prevent Widget::Deactivate() from
-  // undesirably activating another window when tests are being run in parallel.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
-  ShowUi("default");
-  ASSERT_EQ(2u, active_browser_list_->size());
-  Browser* active_window = active_browser_list_->get(0);
-  Browser* inactive_window = active_browser_list_->get(1);
-  views::test::WidgetActivationWaiter waiter(
-      BrowserView::GetBrowserViewForBrowser(inactive_window)->frame(), false);
-  BrowserView::GetBrowserViewForBrowser(active_window)->Activate();
-  waiter.Wait();
-  ASSERT_FALSE(
-      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
-  HoverMouseOverTabAt(0);
-  ASSERT_FALSE(
-      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
-}
-
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewBrowserTest,
-                       HoverCardsSeenRatioMetric) {
-  tab_strip()->AddTabAt(1, TabRendererData());
-  tab_strip()->AddTabAt(2, TabRendererData());
-
-  HoverMouseOverTabAt(0);
-
-  Widget* const widget = hover_card()->GetWidget();
-  ASSERT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-
-  EXPECT_EQ(GetHoverCardsSeenCount(), 1);
-  EXPECT_TRUE(widget->IsVisible());
-
-  HoverMouseOverTabAt(1);
-  EXPECT_EQ(GetHoverCardsSeenCount(), 2);
-  EXPECT_TRUE(widget->IsVisible());
-
-  ui::ListSelectionModel selection;
-  selection.SetSelectedIndex(1);
-  tab_strip()->SetSelection(selection);
-  EXPECT_EQ(GetHoverCardsSeenCount(), 0);
-  EXPECT_FALSE(widget->IsVisible());
-}
-
-// Tests for tabs showing interstitials to check whether the URL in the hover
-// card is displayed or hidden as appropriate.
-class TabHoverCardBubbleViewInterstitialBrowserTest
-    : public TabHoverCardBubbleViewBrowserTest {
- public:
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-
-    https_server_mismatched_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    https_server_mismatched_->SetSSLConfig(
-        net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
-    https_server_mismatched_->AddDefaultHandlers(GetChromeTestDataDir());
-
-    TabHoverCardBubbleViewBrowserTest::SetUpOnMainThread();
-    reputation::InitializeSafetyTipConfig();
-  }
-
-  net::EmbeddedTestServer* https_server_mismatched() {
-    return https_server_mismatched_.get();
-  }
-
- private:
-  std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_;
-};
-
-// Verify that the domain field of tab's hover card is empty if the tab is
-// showing a lookalike interstitial is ("Did you mean google.com?").
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInterstitialBrowserTest,
-                       LookalikeInterstitial_ShouldHideHoverCardUrl) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  // Navigate the tab to a lookalike URL and check the hover card. The domain
-  // field must be empty.
-  content::WebContents* tab =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("googlé.com", "/empty.html")));
-  ASSERT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(tab));
-
-  // Open another tab.
-  chrome::NewTab(browser());
-  HoverMouseOverTabAt(0);
-  views::test::WidgetVisibleWaiter(hover_card()->GetWidget()).Wait();
-
-  EXPECT_TRUE(GetHoverCardDomain().empty());
-  EXPECT_EQ(GetHoverCardsSeenCount(), 1);
-  ASSERT_NE(nullptr, hover_card()->GetWidget());
-  EXPECT_TRUE(hover_card()->GetWidget()->IsVisible());
-}
-
-// Verify that the domain field of tab's hover card is not empty on other types
-// of interstitials (here, SSL).
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInterstitialBrowserTest,
-                       SSLInterstitial_ShouldShowHoverCardUrl) {
-  ASSERT_TRUE(https_server_mismatched()->Start());
-  // Navigate the tab to an SSL error.
-  const GURL url =
-      https_server_mismatched()->GetURL("site.test", "/empty.html");
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  // Open another tab.
-  chrome::NewTab(browser());
-  HoverMouseOverTabAt(0);
-  views::test::WidgetVisibleWaiter(hover_card()->GetWidget()).Wait();
-
-  EXPECT_EQ(base::UTF8ToUTF16(net::GetHostAndPort(url)), GetHoverCardDomain());
-  EXPECT_EQ(GetHoverCardsSeenCount(), 1);
-  ASSERT_NE(nullptr, hover_card()->GetWidget());
-  EXPECT_TRUE(hover_card()->GetWidget()->IsVisible());
-}
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_dialog_browsertest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_dialog_browsertest.cc
new file mode 100644
index 0000000..f6656a65
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_dialog_browsertest.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+
+#include "base/notreached.h"
+#include "chrome/browser/ui/tabs/tab_renderer_data.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_test_util.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "content/public/test/browser_test.h"
+#include "url/gurl.h"
+
+namespace {
+constexpr char16_t kTabTitle[] = u"Test Tab 2";
+constexpr char16_t kTabDomain[] = u"example.com";
+constexpr char kTabUrl[] = "http://example.com/path/to/document.html";
+}  // namespace
+
+class TabHoverCardBubbleViewDialogBrowserTest
+    : public DialogBrowserTest,
+      public test::TabHoverCardTestUtil {
+ public:
+  ~TabHoverCardBubbleViewDialogBrowserTest() override = default;
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    TabRendererData new_tab_data = TabRendererData();
+    new_tab_data.title = kTabTitle;
+    new_tab_data.last_committed_url = GURL(kTabUrl);
+    GetTabStrip(browser())->AddTabAt(1, new_tab_data);
+
+    SimulateHoverTab(browser(), 1);
+  }
+
+  bool VerifyUi() override {
+    if (!DialogBrowserTest::VerifyUi())
+      return false;
+
+    TabStrip* const tab_strip = GetTabStrip(browser());
+    Tab* const tab = tab_strip->tab_at(1);
+    if (!tab) {
+      NOTREACHED();
+      return false;
+    }
+    TabHoverCardBubbleView* const hover_card = GetHoverCard(tab_strip);
+    if (!hover_card) {
+      NOTREACHED();
+      return false;
+    }
+
+    EXPECT_EQ(kTabTitle, hover_card->GetTitleTextForTesting());
+    EXPECT_EQ(kTabDomain, hover_card->GetDomainTextForTesting());
+    EXPECT_EQ(tab, hover_card->GetAnchorView());
+    return true;
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewDialogBrowserTest,
+                       InvokeUi_tab_hover_card) {
+  set_baseline("3907325");
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc
deleted file mode 100644
index abc6e9a9..0000000
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "build/build_config.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/views/tabs/tab.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/interactive_test_utils.h"
-#include "content/public/test/browser_test.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/views/test/widget_test.h"
-#include "ui/views/widget/widget.h"
-
-using views::Widget;
-
-class TabHoverCardBubbleViewInteractiveUiTest : public InProcessBrowserTest {
- public:
-  TabHoverCardBubbleViewInteractiveUiTest() {
-    TabHoverCardController::disable_animations_for_testing_ = true;
-  }
-  TabHoverCardBubbleViewInteractiveUiTest(
-      const TabHoverCardBubbleViewInteractiveUiTest&) = delete;
-  TabHoverCardBubbleViewInteractiveUiTest& operator=(
-      const TabHoverCardBubbleViewInteractiveUiTest&) = delete;
-  ~TabHoverCardBubbleViewInteractiveUiTest() override = default;
-
-  static TabHoverCardBubbleView* GetHoverCard(const TabStrip* tabstrip) {
-    return tabstrip->hover_card_controller_->hover_card_;
-  }
-};
-
-#if defined(USE_AURA)
-
-namespace {
-
-// Similar to views::test::WidgetDestroyedWaiter but waiting after the widget
-// has been closed is a no-op rather than an error.
-class SafeWidgetDestroyedWaiter : public views::WidgetObserver {
- public:
-  explicit SafeWidgetDestroyedWaiter(views::Widget* widget) {
-    observation_.Observe(widget);
-  }
-
-  // views::WidgetObserver:
-  void OnWidgetDestroyed(Widget* widget) override {
-    observation_.Reset();
-    if (!quit_closure_.is_null())
-      std::move(quit_closure_).Run();
-  }
-
-  void Wait() {
-    if (!observation_.IsObserving())
-      return;
-    DCHECK(quit_closure_.is_null());
-    quit_closure_ = run_loop_.QuitClosure();
-    run_loop_.Run();
-  }
-
- private:
-  base::RunLoop run_loop_;
-  base::OnceClosure quit_closure_;
-  base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{
-      this};
-};
-
-}  // namespace
-
-// Verify that the hover card is not visible when any key is pressed.
-IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInteractiveUiTest,
-                       HoverCardHidesOnAnyKeyPressInSameWindow) {
-  TabStrip* tab_strip =
-      BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
-  Tab* tab = tab_strip->tab_at(0);
-  tab_strip->UpdateHoverCard(tab,
-                             TabSlotController::HoverCardUpdateType::kHover);
-  TabHoverCardBubbleView* hover_card = GetHoverCard(tab_strip);
-  Widget* widget = hover_card->GetWidget();
-  EXPECT_NE(nullptr, widget);
-  views::test::WidgetVisibleWaiter(widget).Wait();
-  EXPECT_TRUE(widget->IsVisible());
-
-  // Verify that the hover card widget is destroyed sometime between now and
-  // when we check afterwards. Depending on platform, the destruction could be
-  // synchronous or asynchronous.
-  SafeWidgetDestroyedWaiter widget_destroyed_waiter(widget);
-
-  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
-                                              false, false, false));
-
-  // Note, fade in/out animations are disabled for testing so this should be
-  // relatively quick.
-  widget_destroyed_waiter.Wait();
-  EXPECT_EQ(nullptr, GetHoverCard(tab_strip));
-}
-#endif
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
index 50cb6d81..33f6779 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller.h
@@ -53,9 +53,16 @@
   void PreventImmediateReshow();
   void TabSelectedViaMouse(Tab* tab);
 
+  TabHoverCardBubbleView* hover_card_for_testing() { return hover_card_.get(); }
+
+  static void set_disable_animations_for_testing(
+      bool disable_animations_for_testing) {
+    disable_animations_for_testing_ = disable_animations_for_testing;
+  }
+
+  TabHoverCardMetrics* metrics_for_testing() const { return metrics_.get(); }
+
  private:
-  friend class TabHoverCardBubbleViewBrowserTest;
-  friend class TabHoverCardBubbleViewInteractiveUiTest;
   friend class TabHoverCardMetrics;
   FRIEND_TEST_ALL_PREFIXES(TabHoverCardControllerTest, ShowWrongTabDoesntCrash);
   FRIEND_TEST_ALL_PREFIXES(TabHoverCardControllerTest,
@@ -112,8 +119,6 @@
   void OnMemoryPressureChanged(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
-  TabHoverCardMetrics* metrics_for_testing() const { return metrics_.get(); }
-
   bool waiting_for_preview() const {
     return thumbnail_wait_state_ != ThumbnailWaitState::kNotWaiting;
   }
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
new file mode 100644
index 0000000..374f9cf
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc
@@ -0,0 +1,334 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
+#include "chrome/browser/ui/browser_command_controller.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_close_button.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_test_util.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/reputation/core/safety_tip_test_utils.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "net/base/url_util.h"
+#include "net/dns/mock_host_resolver.h"
+#include "ui/base/models/list_selection_model.h"
+#include "ui/base/test/ui_controls.h"
+#include "ui/display/display.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/views/test/widget_test.h"
+#include "url/gurl.h"
+
+namespace {
+constexpr char16_t kTabTitle[] = u"Test Tab 2";
+constexpr char16_t kTabDomain[] = u"example.com";
+constexpr char kTabUrl[] = "http://example.com/path/to/document.html";
+}  // namespace
+
+class TabHoverCardInteractiveUiTest : public InProcessBrowserTest,
+                                      public test::TabHoverCardTestUtil {
+ public:
+  ~TabHoverCardInteractiveUiTest() override = default;
+
+  // Start the test by moving the mouse to a location where it will not be
+  // hovering the tabstrip. All subsequent interactions will be simulated.
+  void SetUpOnMainThread() override {
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    gfx::Point upper_left;
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+    // Because Ozone makes it impossible to target a point not in a window in
+    // tests, instead target the extreme upper left of the browser window.
+    upper_left = browser()->window()->GetBounds().origin();
+#endif
+    ui_controls::SendMouseMoveNotifyWhenDone(upper_left.x(), upper_left.y(),
+                                             run_loop.QuitClosure());
+    run_loop.Run();
+  }
+};
+
+#if defined(USE_AURA)
+
+// Verify that the hover card is not visible when any key is pressed.
+// Because this test depends on Aura event handling, it is not performed on Mac.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       HoverCardHidesOnAnyKeyPressInSameWindow) {
+  SimulateHoverTab(browser(), 0);
+
+  // Verify that the hover card widget is destroyed sometime between now and
+  // when we check afterwards. Depending on platform, the destruction could be
+  // synchronous or asynchronous.
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  HoverCardDestroyedWaiter waiter(tab_strip);
+
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
+                                              false, false, false));
+
+  // Note, fade in/out animations are disabled for testing so this should be
+  // relatively quick.
+  waiter.Wait();
+  EXPECT_FALSE(IsHoverCardVisible(tab_strip));
+}
+
+#endif
+
+// TODO(crbug.com/1050765): test may be flaky on Windows.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       HoverCardHidesOnMouseExit) {
+  SimulateHoverTab(browser(), 0);
+
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  HoverCardDestroyedWaiter waiter(tab_strip);
+  ui::MouseEvent stop_hover_event(ui::ET_MOUSE_EXITED, gfx::Point(),
+                                  gfx::Point(), base::TimeTicks(), ui::EF_NONE,
+                                  0);
+  static_cast<views::View*>(tab_strip)->OnMouseExited(stop_hover_event);
+  waiter.Wait();
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Linux and/or ChromeOS.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       HoverCardShownOnTabFocus) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  Tab* const tab = tab_strip->tab_at(0);
+  tab_strip->GetFocusManager()->SetFocusedView(tab);
+  WaitForHoverCardVisible(tab_strip);
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Linux and/or ChromeOS.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       HoverCardVisibleOnTabCloseButtonFocusAfterTabFocus) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  Tab* const tab = tab_strip->tab_at(0);
+  tab_strip->GetFocusManager()->SetFocusedView(tab);
+  WaitForHoverCardVisible(tab_strip);
+  tab_strip->GetFocusManager()->SetFocusedView(tab->close_button_);
+  EXPECT_TRUE(IsHoverCardVisible(tab_strip));
+}
+
+// Verify hover card is visible when tab is focused and a key is pressed.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       WidgetVisibleOnKeyPressAfterTabFocus) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  Tab* const tab = tab_strip->tab_at(0);
+  tab_strip->GetFocusManager()->SetFocusedView(tab);
+  WaitForHoverCardVisible(tab_strip);
+
+  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0);
+  tab->OnKeyPressed(key_event);
+  EXPECT_TRUE(IsHoverCardVisible(tab_strip));
+}
+
+// Verify hover card is not visible when tab is focused and the mouse is
+// pressed.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       WidgetNotVisibleOnMousePressAfterTabFocus) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  Tab* const tab = tab_strip->tab_at(0);
+  tab_strip->GetFocusManager()->SetFocusedView(tab);
+  WaitForHoverCardVisible(tab_strip);
+
+  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             base::TimeTicks(), ui::EF_NONE, 0);
+  tab->OnMousePressed(click_event);
+  EXPECT_FALSE(IsHoverCardVisible(tab_strip));
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Windows.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       WidgetNotVisibleOnMousePressAfterHover) {
+  SimulateHoverTab(browser(), 0);
+
+  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             base::TimeTicks(), ui::EF_NONE, 0);
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  tab_strip->tab_at(0)->OnMousePressed(click_event);
+  EXPECT_FALSE(IsHoverCardVisible(tab_strip));
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Linux and/or ChromeOS.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       HoverCardVisibleOnTabFocusFromKeyboardAccelerator) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  TabRendererData new_tab_data = TabRendererData();
+  new_tab_data.title = kTabTitle;
+  new_tab_data.last_committed_url = GURL(kTabUrl);
+  tab_strip->AddTabAt(1, new_tab_data);
+
+  // Cycle focus until it reaches a tab.
+  while (!tab_strip->IsFocusInTabs())
+    browser()->command_controller()->ExecuteCommand(IDC_FOCUS_NEXT_PANE);
+
+  WaitForHoverCardVisible(tab_strip);
+
+  // Move focus forward to the close button or next tab dependent on window
+  // size.
+  tab_strip->AcceleratorPressed(ui::Accelerator(ui::VKEY_RIGHT, ui::EF_NONE));
+  EXPECT_TRUE(IsHoverCardVisible(tab_strip));
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Windows.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       InactiveWindowStaysInactiveOnHover) {
+  const BrowserList* active_browser_list = BrowserList::GetInstance();
+
+  // Open a second browser window.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
+  ASSERT_EQ(2u, active_browser_list->size());
+
+  // Choose one browser to be active; the other to be inactive.
+  Browser* active_window = active_browser_list->get(0);
+  Browser* inactive_window = active_browser_list->get(1);
+
+  // Activate the active browser and wait for the inactive browser to be
+  // inactive.
+  views::test::WidgetActivationWaiter waiter(
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->frame(), false);
+  BrowserView::GetBrowserViewForBrowser(active_window)->Activate();
+  waiter.Wait();
+  ASSERT_FALSE(
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
+
+  // Simulate hovering the inactive tabstrip and wait for the hover card to
+  // appear. The inactive browser should remain inactive.
+  SimulateHoverTab(inactive_window, 0);
+  ASSERT_FALSE(
+      BrowserView::GetBrowserViewForBrowser(inactive_window)->IsActive());
+}
+
+// TODO(crbug.com/1050765): test may be flaky on Windows.
+IN_PROC_BROWSER_TEST_F(TabHoverCardInteractiveUiTest,
+                       UpdatesHoverCardOnHoverDifferentTab) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  TabRendererData new_tab_data = TabRendererData();
+  new_tab_data.title = kTabTitle;
+  new_tab_data.last_committed_url = GURL(kTabUrl);
+  tab_strip->AddTabAt(1, new_tab_data);
+
+  SimulateHoverTab(browser(), 0);
+
+  auto* const hover_card = SimulateHoverTab(browser(), 1);
+  EXPECT_EQ(kTabTitle, hover_card->GetTitleTextForTesting());
+  EXPECT_EQ(kTabDomain, hover_card->GetDomainTextForTesting());
+  EXPECT_EQ(tab_strip->tab_at(1), hover_card->GetAnchorView());
+}
+
+using TabHoverCardBubbleViewMetricsTest = TabHoverCardInteractiveUiTest;
+
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewMetricsTest,
+                       HoverCardsSeenRatioMetric) {
+  TabStrip* const tab_strip = GetTabStrip(browser());
+  tab_strip->AddTabAt(1, TabRendererData());
+  tab_strip->AddTabAt(2, TabRendererData());
+
+  SimulateHoverTab(browser(), 0);
+
+  EXPECT_EQ(1, GetHoverCardsSeenCount(browser()));
+
+  SimulateHoverTab(browser(), 1);
+
+  EXPECT_EQ(2, GetHoverCardsSeenCount(browser()));
+
+  ui::ListSelectionModel selection;
+  selection.SetSelectedIndex(1);
+  tab_strip->SetSelection(selection);
+
+  TabHoverCardBubbleView* const hover_card = GetHoverCard(tab_strip);
+  EXPECT_FALSE(hover_card && hover_card->GetWidget()->IsVisible());
+  EXPECT_EQ(0, GetHoverCardsSeenCount(browser()));
+}
+
+// Tests for tabs showing interstitials to check whether the URL in the hover
+// card is displayed or hidden as appropriate.
+class TabHoverCardBubbleViewInterstitialBrowserTest
+    : public TabHoverCardInteractiveUiTest {
+ public:
+  ~TabHoverCardBubbleViewInterstitialBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+
+    https_server_mismatched_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server_mismatched_->SetSSLConfig(
+        net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+    https_server_mismatched_->AddDefaultHandlers(GetChromeTestDataDir());
+
+    InProcessBrowserTest::SetUpOnMainThread();
+    reputation::InitializeSafetyTipConfig();
+  }
+
+  net::EmbeddedTestServer* https_server_mismatched() {
+    return https_server_mismatched_.get();
+  }
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_;
+};
+
+// Verify that the domain field of tab's hover card is empty if the tab is
+// showing a lookalike interstitial is ("Did you mean google.com?").
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInterstitialBrowserTest,
+                       LookalikeInterstitial_ShouldHideHoverCardUrl) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // Navigate the tab to a lookalike URL and check the hover card. The domain
+  // field must be empty.
+  static constexpr char kLookalikeDomain[] = "googlé.com";
+  static constexpr char kUrlPath[] = "/empty.html";
+  const GURL url = embedded_test_server()->GetURL(kLookalikeDomain, kUrlPath);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  content::WebContents* const tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(tab));
+
+  // Open another tab.
+  chrome::NewTab(browser());
+  auto* const hover_card = SimulateHoverTab(browser(), 0);
+
+  EXPECT_TRUE(hover_card->GetDomainTextForTesting().empty());
+  EXPECT_EQ(1, GetHoverCardsSeenCount(browser()));
+}
+
+// Verify that the domain field of tab's hover card is not empty on other types
+// of interstitials (here, SSL).
+IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInterstitialBrowserTest,
+                       SSLInterstitial_ShouldShowHoverCardUrl) {
+  ASSERT_TRUE(https_server_mismatched()->Start());
+  // Navigate the tab to an SSL error.
+  static constexpr char kBadSSLDomain[] = "site.test";
+  static constexpr char kUrlPath[] = "/empty.html";
+  const GURL url = https_server_mismatched()->GetURL(kBadSSLDomain, kUrlPath);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  content::WebContents* const tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(tab));
+
+  // Open another tab.
+  chrome::NewTab(browser());
+  auto* const hover_card = SimulateHoverTab(browser(), 0);
+
+  EXPECT_EQ(base::UTF8ToUTF16(net::GetHostAndPort(url)),
+            hover_card->GetDomainTextForTesting());
+  EXPECT_EQ(1, GetHoverCardsSeenCount(browser()));
+}
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_test_util.cc b/chrome/browser/ui/views/tabs/tab_hover_card_test_util.cc
new file mode 100644
index 0000000..f0ad053
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_test_util.cc
@@ -0,0 +1,101 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/tabs/tab_hover_card_test_util.h"
+
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "ui/views/test/widget_test.h"
+
+namespace test {
+
+TabHoverCardTestUtil::TabHoverCardTestUtil()
+    : animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
+          gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) {
+  TabHoverCardController::set_disable_animations_for_testing(true);
+}
+
+TabHoverCardTestUtil::~TabHoverCardTestUtil() {
+  TabHoverCardController::set_disable_animations_for_testing(false);
+}
+
+// static
+TabStrip* TabHoverCardTestUtil::GetTabStrip(Browser* browser) {
+  return BrowserView::GetBrowserViewForBrowser(browser)->tabstrip();
+}
+
+// static
+TabHoverCardBubbleView* TabHoverCardTestUtil::GetHoverCard(
+    TabStrip* tab_strip) {
+  return tab_strip->hover_card_controller_for_testing()
+      ->hover_card_for_testing();
+}
+
+// static
+TabHoverCardBubbleView* TabHoverCardTestUtil::WaitForHoverCardVisible(
+    TabStrip* tab_strip) {
+  auto* const hover_card = GetHoverCard(tab_strip);
+  views::test::WidgetVisibleWaiter(hover_card->GetWidget()).Wait();
+  return hover_card;
+}
+
+// static
+bool TabHoverCardTestUtil::IsHoverCardVisible(TabStrip* tab_strip) {
+  auto* const hover_card = GetHoverCard(tab_strip);
+  return hover_card && hover_card->GetWidget() &&
+         hover_card->GetWidget()->IsVisible();
+}
+
+// static
+int TabHoverCardTestUtil::GetHoverCardsSeenCount(Browser* browser) {
+  return GetTabStrip(browser)
+      ->hover_card_controller_for_testing()
+      ->metrics_for_testing()
+      ->cards_seen_count();
+}
+
+// static
+TabHoverCardBubbleView* TabHoverCardTestUtil::SimulateHoverTab(Browser* browser,
+                                                               int tab_index) {
+  auto* const tab_strip = GetTabStrip(browser);
+
+  // We don't use Tab::OnMouseEntered here to invoke the hover card because
+  // that path is disabled in browser tests. If we enabled it, the real mouse
+  // might interfere with the test.
+  tab_strip->UpdateHoverCard(tab_strip->tab_at(tab_index),
+                             TabSlotController::HoverCardUpdateType::kHover);
+
+  return WaitForHoverCardVisible(tab_strip);
+}
+
+TabHoverCardTestUtil::HoverCardDestroyedWaiter::HoverCardDestroyedWaiter(
+    TabStrip* tab_strip) {
+  auto* const hover_card = GetHoverCard(tab_strip);
+  if (hover_card && hover_card->GetWidget())
+    observation_.Observe(hover_card->GetWidget());
+}
+
+TabHoverCardTestUtil::HoverCardDestroyedWaiter::~HoverCardDestroyedWaiter() =
+    default;
+
+void TabHoverCardTestUtil::HoverCardDestroyedWaiter::Wait() {
+  if (!observation_.IsObserving())
+    return;
+  DCHECK(quit_closure_.is_null());
+  quit_closure_ = run_loop_.QuitClosure();
+  run_loop_.Run();
+}
+
+// views::WidgetObserver:
+void TabHoverCardTestUtil::HoverCardDestroyedWaiter::OnWidgetDestroyed(
+    views::Widget* widget) {
+  observation_.Reset();
+  if (!quit_closure_.is_null())
+    std::move(quit_closure_).Run();
+}
+
+}  // namespace test
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_test_util.h b/chrome/browser/ui/views/tabs/tab_hover_card_test_util.h
new file mode 100644
index 0000000..37e2f57
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_test_util.h
@@ -0,0 +1,69 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_TEST_UTIL_H_
+
+#include <memory>
+
+#include "base/auto_reset.h"
+#include "base/run_loop.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
+#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "ui/gfx/animation/animation_test_api.h"
+#include "ui/views/widget/widget.h"
+
+namespace test {
+
+// Class that disables hover card animations and provides a lot of convenience
+// methods that can be used across various tab hover card tests. Your test
+// fixture should inherit this class.
+class TabHoverCardTestUtil {
+ public:
+  TabHoverCardTestUtil();
+  virtual ~TabHoverCardTestUtil();
+  TabHoverCardTestUtil(const TabHoverCardTestUtil&) = delete;
+  void operator=(const TabHoverCardTestUtil&) = delete;
+
+  static TabStrip* GetTabStrip(Browser* browser);
+  static TabHoverCardBubbleView* GetHoverCard(TabStrip* tab_strip);
+  static TabHoverCardBubbleView* WaitForHoverCardVisible(TabStrip* tab_strip);
+  static bool IsHoverCardVisible(TabStrip* tab_strip);
+  static int GetHoverCardsSeenCount(Browser* browser);
+  static TabHoverCardBubbleView* SimulateHoverTab(Browser* browser,
+                                                  int tab_index);
+
+  // Similar to views::test::WidgetDestroyedWaiter but for the hover card for
+  // TabStrip. Waiting after the hover card's widget is destroyed (or if there
+  // is no hover card) is a no-op rather than an error.
+  class HoverCardDestroyedWaiter : public views::WidgetObserver {
+   public:
+    explicit HoverCardDestroyedWaiter(TabStrip* tab_strip);
+    ~HoverCardDestroyedWaiter() override;
+    HoverCardDestroyedWaiter(const HoverCardDestroyedWaiter&) = delete;
+    void operator=(const HoverCardDestroyedWaiter&) = delete;
+
+    // Waits for the hover card and its widget to be destroyed; is a no-op if
+    void Wait();
+
+   private:
+    // views::WidgetObserver:
+    void OnWidgetDestroyed(views::Widget* widget) override;
+
+    base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
+    base::OnceClosure quit_closure_;
+    base::ScopedObservation<views::Widget, views::WidgetObserver> observation_{
+        this};
+  };
+
+ private:
+  std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>>
+      animation_mode_reset_;
+};
+
+}  // namespace test
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_HOVER_CARD_TEST_UTIL_H_
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index f243d98a..4b1c521 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -327,14 +327,16 @@
       gfx::Point loc_in_local_coords) override;
   views::View* GetViewForDrop() override;
 
+  TabHoverCardController* hover_card_controller_for_testing() {
+    return hover_card_controller_.get();
+  }
+
  private:
   class TabDragContextImpl;
 
   friend class TabDragControllerTest;
   friend class TabDragContextImpl;
   friend class TabGroupEditorBubbleViewDialogBrowserTest;
-  friend class TabHoverCardBubbleViewBrowserTest;
-  friend class TabHoverCardBubbleViewInteractiveUiTest;
   friend class TabStripTestBase;
   friend class TabStripRegionViewTestBase;
 
diff --git a/chrome/browser/ui/webauthn/account_hover_list_model.h b/chrome/browser/ui/webauthn/account_hover_list_model.h
index 1ca9c9b..97a1ce7 100644
--- a/chrome/browser/ui/webauthn/account_hover_list_model.h
+++ b/chrome/browser/ui/webauthn/account_hover_list_model.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_WEBAUTHN_ACCOUNT_HOVER_LIST_MODEL_H_
 
 #include <stddef.h>
+#include <string>
 #include <vector>
 
 #include "base/containers/span.h"
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index c211856..0753d427 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -31,9 +31,9 @@
     : public AuthenticatorRequestSheetModel,
       public AuthenticatorRequestDialogModel::Observer {
  public:
-  // Determines whether the button in the lower-left corner of the dialog to
-  // display other available mechanisms is shown on a sheet.
-  enum OtherMechanismButtonVisibility {
+  // Determines whether the button in the lower-left corner of the dialog, to
+  // display other available mechanisms, is shown on a sheet.
+  enum class OtherMechanismButtonVisibility {
     // The button is not shown (default).
     kHidden,
     // The button is shown if there is more than one mechanism to choose from.
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/BUILD.gn b/chrome/browser/ui/webui/ash/cloud_upload/BUILD.gn
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/BUILD.gn
rename to chrome/browser/ui/webui/ash/cloud_upload/BUILD.gn
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/OWNERS b/chrome/browser/ui/webui/ash/cloud_upload/OWNERS
similarity index 100%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/OWNERS
rename to chrome/browser/ui/webui/ash/cloud_upload/OWNERS
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
similarity index 95%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
index 9e8e43a..f7dd7dc 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module chromeos.cloud_upload.mojom;
+module ash.cloud_upload.mojom;
 
 import "mojo/public/mojom/base/file_path.mojom";
 
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
similarity index 94%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
index 9fd0d9a..6f4de64a 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -13,11 +13,11 @@
 #include "chrome/browser/ash/file_manager/file_tasks.h"
 #include "chrome/browser/ash/file_manager/open_with_browser.h"
 #include "chrome/browser/ash/file_system_provider/mount_path_util.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h"
 #include "chrome/common/webui_url_constants.h"
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 namespace {
 
 using file_manager::file_tasks::kDriveTaskResultMetricName;
@@ -162,4 +162,4 @@
   return false;
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
similarity index 83%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
index df8fb0d..27472d9 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
 
 #include <vector>
 
@@ -12,7 +12,7 @@
 
 class Profile;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 // Either OneDrive for the Office PWA or Drive for Web Drive editing.
 enum class UploadType {
@@ -20,7 +20,7 @@
   kDrive,
 };
 
-// The string conversions of chromeos::cloud_upload::mojom::UserAction.
+// The string conversions of ash::cloud_upload::mojom::UserAction.
 const char kUserActionCancel[] = "cancel";
 const char kUserActionUpload[] = "upload";
 
@@ -61,6 +61,6 @@
   UploadRequestCallback callback_;
 };
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_DIALOG_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
similarity index 96%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
index d86182e..6a7e460f 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
 
 #include "ash/public/cpp/notification_utils.h"
 #include "base/memory/scoped_refptr.h"
@@ -11,7 +11,7 @@
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/message_center/public/cpp/notification_delegate.h"
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 namespace {
 
 constexpr char kCloudUploadProgressNotificationId[] = "cloud-upload-progress";
@@ -106,4 +106,4 @@
   }
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
similarity index 81%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
index fe93698..17ab056f 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
 
 #include <memory>
 
@@ -14,7 +14,7 @@
 
 class Profile;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 // Manages creation/deletion and update of cloud upload system notifications.
 class CloudUploadNotificationManager
@@ -50,6 +50,6 @@
   base::WeakPtrFactory<CloudUploadNotificationManager> weak_ptr_factory_{this};
 };
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
similarity index 68%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
index 5bd7387d..20745c9 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.cc
@@ -2,15 +2,14 @@
 // 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/chromeos/cloud_upload/cloud_upload_page_handler.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h"
 
 #include "base/files/file_path.h"
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 CloudUploadPageHandler::CloudUploadPageHandler(
-    mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandler>
-        pending_page_handler,
+    mojo::PendingReceiver<mojom::PageHandler> pending_page_handler,
     RespondAndCloseCallback callback)
     : receiver_{this, std::move(pending_page_handler)},
       callback_{std::move(callback)} {}
@@ -23,4 +22,4 @@
   }
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h
new file mode 100644
index 0000000..2740f348
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom-shared.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace ash::cloud_upload {
+
+// Handles communication from the chrome://cloud-upload renderer process to
+// the browser process exposing various methods for the JS to invoke.
+class CloudUploadPageHandler : public mojom::PageHandler {
+ public:
+  using RespondAndCloseCallback =
+      base::OnceCallback<void(mojom::UserAction action)>;
+  explicit CloudUploadPageHandler(
+      mojo::PendingReceiver<mojom::PageHandler> pending_page_handler,
+      RespondAndCloseCallback callback);
+
+  CloudUploadPageHandler(const CloudUploadPageHandler&) = delete;
+  CloudUploadPageHandler& operator=(const CloudUploadPageHandler&) = delete;
+
+  ~CloudUploadPageHandler() override;
+
+  // mojom::PageHandler:
+  void RespondAndClose(mojom::UserAction action) override;
+
+ private:
+  mojo::Receiver<PageHandler> receiver_;
+  RespondAndCloseCallback callback_;
+
+  base::WeakPtrFactory<CloudUploadPageHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace ash::cloud_upload
+
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
similarity index 80%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
index 8c6161d1..be9d39d 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.cc
@@ -2,11 +2,11 @@
 // 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/chromeos/cloud_upload/cloud_upload_ui.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h"
 
 #include "base/logging.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_dialog.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/cloud_upload_resources.h"
@@ -14,7 +14,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 CloudUploadUI::CloudUploadUI(content::WebUI* web_ui)
     : ui::MojoWebDialogUI{web_ui} {
@@ -28,8 +28,7 @@
 CloudUploadUI::~CloudUploadUI() = default;
 
 void CloudUploadUI::BindInterface(
-    mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandlerFactory>
-        pending_receiver) {
+    mojo::PendingReceiver<mojom::PageHandlerFactory> pending_receiver) {
   if (factory_receiver_.is_bound()) {
     factory_receiver_.reset();
   }
@@ -37,8 +36,7 @@
 }
 
 void CloudUploadUI::CreatePageHandler(
-    mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandler>
-        receiver) {
+    mojo::PendingReceiver<mojom::PageHandler> receiver) {
   page_handler_ = std::make_unique<CloudUploadPageHandler>(
       std::move(receiver),
       // base::Unretained() because |page_handler_| will not out-live |this|.
@@ -61,4 +59,4 @@
 
 WEB_UI_CONTROLLER_TYPE_IMPL(CloudUploadUI)
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h
new file mode 100644
index 0000000..1f63272e
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
+
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom-shared.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload.mojom.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_page_handler.h"
+#include "ui/web_dialogs/web_dialog_ui.h"
+
+namespace ash::cloud_upload {
+
+// The UI for chrome://cloud-upload, used for uploading files to the cloud.
+class CloudUploadUI : public ui::MojoWebDialogUI,
+                      public mojom::PageHandlerFactory {
+ public:
+  explicit CloudUploadUI(content::WebUI* web_ui);
+  CloudUploadUI(const CloudUploadUI&) = delete;
+  CloudUploadUI& operator=(const CloudUploadUI&) = delete;
+
+  ~CloudUploadUI() override;
+
+  // Instantiates implementor of the mojom::PageHandlerFactory
+  // mojo interface passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<mojom::PageHandlerFactory> pending_receiver);
+
+  // mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingReceiver<mojom::PageHandler> pending_page_handler) override;
+
+ private:
+  void RespondAndCloseDialog(mojom::UserAction action);
+
+  std::unique_ptr<CloudUploadPageHandler> page_handler_;
+  mojo::Receiver<mojom::PageHandlerFactory> factory_receiver_{this};
+
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+}  // namespace ash::cloud_upload
+
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
similarity index 93%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
index 2d2f9e8..2973f59 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.cc
@@ -2,11 +2,11 @@
 // 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/chromeos/cloud_upload/cloud_upload_util.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "content/public/browser/browser_thread.h"
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 storage::FileSystemURL FilePathToFileSystemURL(
     Profile* profile,
@@ -59,4 +59,4 @@
              << error_string;
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
similarity index 79%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.h
rename to chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
index d45ec21..b99ab103 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
 
 #include "base/callback.h"
 #include "base/files/file.h"
@@ -14,7 +14,7 @@
 
 class Profile;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 // Converts an absolute FilePath into a filesystem URL.
 storage::FileSystemURL FilePathToFileSystemURL(
@@ -33,6 +33,6 @@
 // to log any encountered error.
 void LogErrorOnShowItemInFolder(platform_util::OpenOperationResult result);
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_UTIL_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
similarity index 97%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
index 61e9bd5..fa00f1b 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h"
 
 #include "base/task/thread_pool.h"
 #include "base/timer/timer.h"
@@ -10,14 +10,14 @@
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/ash/file_manager/open_util.h"
 #include "chrome/browser/ash/file_manager/volume_manager.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/common/task_util.h"
 
 using storage::FileSystemURL;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 namespace {
 
 // The default folder where the file should be uploaded.
@@ -350,4 +350,4 @@
                      weak_ptr_factory_.GetWeakPtr(), /*timed_out=*/true));
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
similarity index 90%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.h
rename to chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
index fc1d04de..ea79241 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/drive_upload_handler.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
 
 #include <memory>
 
@@ -14,7 +14,7 @@
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/file_manager/io_task_controller.h"
 #include "chrome/browser/chromeos/extensions/file_manager/scoped_suppress_drive_notifications_for_path.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
 #include "chromeos/ash/components/drivefs/drivefs_host_observer.h"
 #include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
 #include "storage/browser/file_system/file_system_context.h"
@@ -23,7 +23,7 @@
 
 class Profile;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 // Manages the "upload to Drive" workflow after user confirmation on the upload
 // dialog. Instantiated by the static `Upload` method. Starts with moving the
@@ -101,6 +101,6 @@
   base::WeakPtrFactory<DriveUploadHandler> weak_ptr_factory_{this};
 };
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_DRIVE_UPLOAD_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
similarity index 96%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.cc
rename to chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
index 754de81d..52b746c 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h"
 
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/file_manager/copy_or_move_io_task.h"
@@ -11,7 +11,7 @@
 #include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/ash/file_system_provider/service.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_util.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/common/task_util.h"
@@ -21,7 +21,7 @@
 using ash::file_system_provider::Service;
 using storage::FileSystemURL;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 namespace {
 
 const char kODFSExtensionId[] = "ajdgmkbkgifbokednjgbmieaemeighkg";
@@ -207,4 +207,4 @@
   OnEndUpload(uploaded_file_url);
 }
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.h b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
similarity index 87%
rename from chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.h
rename to chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
index 3c4e624..3dd124e 100644
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/one_drive_upload_handler.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
 
 #include <memory>
 
@@ -13,14 +13,14 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/file_manager/io_task_controller.h"
 #include "chrome/browser/platform_util.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_notification_manager.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/file_system/file_system_url.h"
 #include "url/gurl.h"
 
 class Profile;
 
-namespace chromeos::cloud_upload {
+namespace ash::cloud_upload {
 
 // Manages the "upload to OneDrive" workflow after user confirmation on the
 // upload dialog. Instantiated by the static `Upload` method. Starts with moving
@@ -78,6 +78,6 @@
   base::WeakPtrFactory<OneDriveUploadHandler> weak_ptr_factory_{this};
 };
 
-}  // namespace chromeos::cloud_upload
+}  // namespace ash::cloud_upload
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_ONE_DRIVE_UPLOAD_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 9d183cc..4bd980f 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -318,6 +318,7 @@
     "chrome://os-credits",
     "chrome://os-settings",
     "chrome://power",
+    "chrome://projector",
     "chrome://proximity-auth/proximity_auth.html",
     "chrome://set-time",
     "chrome://slow",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 7035c85..1141374 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -252,10 +252,10 @@
 #include "chrome/browser/ui/webui/ash/arc_power_control/arc_power_control_ui.h"
 #include "chrome/browser/ui/webui/ash/audio/audio_ui.h"
 #include "chrome/browser/ui/webui/ash/cellular_setup/mobile_setup_ui.h"
+#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_ui.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_pairing_dialog.h"
 #include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_ui.h"
 #include "chrome/browser/ui/webui/chromeos/cryptohome_ui.h"
@@ -965,7 +965,7 @@
     if (!ash::features::IsUploadOfficeToCloudEnabled()) {
       return nullptr;
     }
-    return &NewWebUI<chromeos::cloud_upload::CloudUploadUI>;
+    return &NewWebUI<ash::cloud_upload::CloudUploadUI>;
   }
   if (url.host_piece() == chrome::kChromeUIAccountManagerErrorHost)
     return &NewWebUI<ash::AccountManagerErrorUI>;
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.h b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.h
deleted file mode 100644
index f92978b..0000000
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom-shared.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace chromeos::cloud_upload {
-
-// Handles communication from the chrome://cloud-upload renderer process to
-// the browser process exposing various methods for the JS to invoke.
-class CloudUploadPageHandler
-    : public chromeos::cloud_upload::mojom::PageHandler {
- public:
-  using RespondAndCloseCallback =
-      base::OnceCallback<void(mojom::UserAction action)>;
-  explicit CloudUploadPageHandler(
-      mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandler>
-          pending_page_handler,
-      RespondAndCloseCallback callback);
-
-  CloudUploadPageHandler(const CloudUploadPageHandler&) = delete;
-  CloudUploadPageHandler& operator=(const CloudUploadPageHandler&) = delete;
-
-  ~CloudUploadPageHandler() override;
-
-  // chromeos::cloud_upload::mojom::PageHandler:
-  void RespondAndClose(mojom::UserAction action) override;
-
- private:
-  mojo::Receiver<chromeos::cloud_upload::mojom::PageHandler> receiver_;
-  RespondAndCloseCallback callback_;
-
-  base::WeakPtrFactory<CloudUploadPageHandler> weak_ptr_factory_{this};
-};
-
-}  // namespace chromeos::cloud_upload
-
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_PAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.h b/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.h
deleted file mode 100644
index 15f9855..0000000
--- a/chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_ui.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
-
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom-shared.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload.mojom.h"
-#include "chrome/browser/ui/webui/chromeos/cloud_upload/cloud_upload_page_handler.h"
-#include "ui/web_dialogs/web_dialog_ui.h"
-
-namespace chromeos::cloud_upload {
-
-// The UI for chrome://cloud-upload, used for uploading files to the cloud.
-class CloudUploadUI : public ui::MojoWebDialogUI,
-                      public chromeos::cloud_upload::mojom::PageHandlerFactory {
- public:
-  explicit CloudUploadUI(content::WebUI* web_ui);
-  CloudUploadUI(const CloudUploadUI&) = delete;
-  CloudUploadUI& operator=(const CloudUploadUI&) = delete;
-
-  ~CloudUploadUI() override;
-
-  // Instantiates implementor of the mojom::PageHandlerFactory
-  // mojo interface passing the pending receiver that will be internally bound.
-  void BindInterface(
-      mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandlerFactory>
-          pending_receiver);
-
-  // chromeos::cloud_upload::mojom::PageHandlerFactory:
-  void CreatePageHandler(
-      mojo::PendingReceiver<chromeos::cloud_upload::mojom::PageHandler>
-          pending_page_handler) override;
-
- private:
-  void RespondAndCloseDialog(mojom::UserAction action);
-
-  std::unique_ptr<CloudUploadPageHandler> page_handler_;
-  mojo::Receiver<chromeos::cloud_upload::mojom::PageHandlerFactory>
-      factory_receiver_{this};
-
-  WEB_UI_CONTROLLER_TYPE_DECL();
-};
-
-}  // namespace chromeos::cloud_upload
-
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_CLOUD_UPLOAD_CLOUD_UPLOAD_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 49c7dbe..797f5307 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -248,8 +248,13 @@
   return family_link_allowed;
 }
 
+// TODO(https://crbug.com/1364455)
+// Make this function fallible when version_loader::GetVersion()
+// returns an optional that is empty
 void GetVersionAndConsent(std::string* out_version, bool* out_consent) {
-  *out_version = version_loader::GetVersion(version_loader::VERSION_SHORT);
+  absl::optional<std::string> version =
+      version_loader::GetVersion(version_loader::VERSION_SHORT);
+  *out_version = version.value_or("0.0.0.0");
   *out_consent = GoogleUpdateSettings::GetCollectStatsConsent();
 }
 
@@ -305,12 +310,12 @@
   // for many times or password changed.
   // TODO(b/197615068): Add metric to record the number of times we prepared for
   // recovery and the number of times recovery is actually required.
-  static const ash::ReauthReason kPossibleReasons[] = {
-      ash::ReauthReason::INCORRECT_PASSWORD_ENTERED,
-      ash::ReauthReason::INVALID_TOKEN_HANDLE,
-      ash::ReauthReason::SYNC_FAILED,
-      ash::ReauthReason::PASSWORD_UPDATE_SKIPPED,
-      ash::ReauthReason::FORGOT_PASSWORD,
+  static constexpr int kPossibleReasons[] = {
+      static_cast<int>(ash::ReauthReason::kIncorrectPasswordEntered),
+      static_cast<int>(ash::ReauthReason::kInvalidTokenHandle),
+      static_cast<int>(ash::ReauthReason::kSyncFailed),
+      static_cast<int>(ash::ReauthReason::kPasswordUpdateSkipped),
+      static_cast<int>(ash::ReauthReason::kForgotPassword),
   };
   user_manager::KnownUser known_user(g_browser_process->local_state());
   absl::optional<int> reauth_reason = known_user.FindReauthReason(account_id);
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc
index 2e9d9143..60cec3d 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
 
-#include <memory>
-#include <string>
 #include <utility>
 
 #include "chrome/browser/profiles/profile.h"
@@ -27,7 +25,8 @@
 
 // static
 ParentAccessDialog::ShowError ParentAccessDialog::Show(
-    parent_access_ui::mojom::ParentAccessParamsPtr params) {
+    parent_access_ui::mojom::ParentAccessParamsPtr params,
+    ParentAccessDialogCallback callback) {
   ParentAccessDialog* current_instance = GetInstance();
   if (current_instance) {
     return ShowError::kDialogAlreadyVisible;
@@ -39,7 +38,8 @@
 
   // Note:  |current_instance|'s memory is freed when
   // SystemWebDialogDelegate::OnDialogClosed() is called.
-  current_instance = new ParentAccessDialog(std::move(params));
+  current_instance =
+      new ParentAccessDialog(std::move(params), std::move(callback));
   current_instance->ShowSystemDialogForBrowserContext(profile);
   return ShowError::kNone;
 }
@@ -73,11 +73,19 @@
 }
 
 ParentAccessDialog::ParentAccessDialog(
-    parent_access_ui::mojom::ParentAccessParamsPtr params)
+    parent_access_ui::mojom::ParentAccessParamsPtr params,
+    ParentAccessDialogCallback callback)
     : SystemWebDialogDelegate(GURL(chrome::kChromeUIParentAccessURL),
                               /*title=*/std::u16string()),
-      parent_access_params_(std::move(params)) {}
+      parent_access_params_(std::move(params)),
+      callback_(std::move(callback)) {}
 
-ParentAccessDialog::~ParentAccessDialog() = default;
+ParentAccessDialog::~ParentAccessDialog() {
+  auto result = std::make_unique<ParentAccessDialog::Result>();
+  // TODO(b/241166361) Return correct ParentAccessDialog::Result based
+  // on result of the flow.
+  result->status = ParentAccessDialog::Result::Status::kCancelled;
+  std::move(callback_).Run(std::move(result));
+}
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h
index 9041b49d..67836563 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h
+++ b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_PARENT_ACCESS_PARENT_ACCESS_DIALOG_H_
 
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
 #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
 #include "chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h"
 
@@ -17,9 +21,29 @@
   // Error state returned by the Show() function.
   enum ShowError { kNone, kDialogAlreadyVisible, kNotAChildUser };
 
+  // The result of the parent access request, passed back to the caller.
+  struct Result {
+    // The status of the result.
+    enum Status {
+      kApproved,   // The parent was verified and they approved.
+      kDeclined,   // The request was explicitly declined by the parent.
+      kCancelled,  // The request was cancelled/dismissed by the parent.
+      kError,      // An error occurred while handling the request.
+    };
+    Status status = kCancelled;
+
+    // The Parent Access Token.  Only set if status is kVerified.
+    std::string parent_access_token = "";
+  };
+
+  // Callback for the result of the dialog.
+  using ParentAccessDialogCallback =
+      base::OnceCallback<void(std::unique_ptr<Result>)>;
+
   // Shows the dialog; if the dialog is already displayed, this returns an
   // error.
-  static ShowError Show(parent_access_ui::mojom::ParentAccessParamsPtr params);
+  static ShowError Show(parent_access_ui::mojom::ParentAccessParamsPtr params,
+                        ParentAccessDialogCallback callback);
 
   static ParentAccessDialog* GetInstance();
 
@@ -40,11 +64,13 @@
 
  protected:
   explicit ParentAccessDialog(
-      parent_access_ui::mojom::ParentAccessParamsPtr params);
+      parent_access_ui::mojom::ParentAccessParamsPtr params,
+      ParentAccessDialogCallback callback);
   ~ParentAccessDialog() override;
 
  private:
   parent_access_ui::mojom::ParentAccessParamsPtr parent_access_params_;
+  ParentAccessDialogCallback callback_;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc
index 6f9af22..3a34dc7 100644
--- a/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog_browsertest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_dialog.h"
 
 #include "ash/shell.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_browsertest_base.h"
 #include "chrome/browser/ui/webui/chromeos/parent_access/parent_access_ui.mojom.h"
@@ -24,12 +26,27 @@
 
 // Verify that the dialog is shown and correctly configured.
 IN_PROC_BROWSER_TEST_F(ParentAccessDialogBrowserTest, ShowDialog) {
+  base::RunLoop run_loop;
+
+  // Create the callback.
+  ParentAccessDialog::ParentAccessDialogCallback callback = base::BindOnce(
+      [](base::OnceClosure closure,
+         std::unique_ptr<
+             chromeos::ParentAccessDialog::ParentAccessDialog::Result> result)
+          -> void {
+        EXPECT_EQ(result->status,
+                  chromeos::ParentAccessDialog::Result::Status::kCancelled);
+        std::move(closure).Run();
+      },
+      run_loop.QuitClosure());
+
   // Show the dialog.
-  ParentAccessDialog::ShowError error =
-      ParentAccessDialog::Show(parent_access_ui::mojom::ParentAccessParams::New(
+  ParentAccessDialog::ShowError error = ParentAccessDialog::Show(
+      parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
-              parent_access_ui::mojom::WebApprovalsParams::New())));
+              parent_access_ui::mojom::WebApprovalsParams::New())),
+      std::move(callback));
 
   // Verify it is showing.
   ASSERT_EQ(error, ParentAccessDialog::ShowError::kNone);
@@ -54,16 +71,22 @@
 
   // The dialog instance should be gone after ESC is pressed.
   EXPECT_EQ(ParentAccessDialog::GetInstance(), nullptr);
+
+  run_loop.Run();
 }
 
 IN_PROC_BROWSER_TEST_F(ParentAccessDialogBrowserTest,
                        ErrorOnDialogAlreadyVisible) {
   // Show the dialog.
-  ParentAccessDialog::ShowError error =
-      ParentAccessDialog::Show(parent_access_ui::mojom::ParentAccessParams::New(
+  ParentAccessDialog::ShowError error = ParentAccessDialog::Show(
+      parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
-              parent_access_ui::mojom::WebApprovalsParams::New())));
+              parent_access_ui::mojom::WebApprovalsParams::New())),
+      base::BindOnce(
+          [](std::unique_ptr<
+              chromeos::ParentAccessDialog::ParentAccessDialog::Result> result)
+              -> void {}));
 
   // Verify it is showing.
   ASSERT_EQ(error, ParentAccessDialog::ShowError::kNone);
@@ -74,11 +97,15 @@
       params->flow_type,
       parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess);
 
-  error =
-      ParentAccessDialog::Show(parent_access_ui::mojom::ParentAccessParams::New(
+  error = ParentAccessDialog::Show(
+      parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
-              parent_access_ui::mojom::WebApprovalsParams::New())));
+              parent_access_ui::mojom::WebApprovalsParams::New())),
+      base::BindOnce(
+          [](std::unique_ptr<
+              chromeos::ParentAccessDialog::ParentAccessDialog::Result> result)
+              -> void {}));
 
   // Verify an error was returned indicating it can't be shown again.
   EXPECT_EQ(error, ParentAccessDialog::ShowError::kDialogAlreadyVisible);
@@ -92,15 +119,22 @@
 IN_PROC_BROWSER_TEST_F(ParentAccessDialogRegularUserBrowserTest,
                        ErrorForNonChildUser) {
   // Show the dialog.
-  ParentAccessDialog::ShowError error =
-      ParentAccessDialog::Show(parent_access_ui::mojom::ParentAccessParams::New(
+  ParentAccessDialog::ShowError error = ParentAccessDialog::Show(
+      parent_access_ui::mojom::ParentAccessParams::New(
           parent_access_ui::mojom::ParentAccessParams::FlowType::kWebsiteAccess,
           parent_access_ui::mojom::FlowTypeParams::NewWebApprovalsParams(
-              parent_access_ui::mojom::WebApprovalsParams::New())));
+              parent_access_ui::mojom::WebApprovalsParams::New())),
+      base::BindOnce(
+          [](std::unique_ptr<
+              chromeos::ParentAccessDialog::ParentAccessDialog::Result> result)
+              -> void {}));
 
   // Verify it is not showing.
   EXPECT_EQ(error, ParentAccessDialog::ShowError::kNotAChildUser);
   EXPECT_EQ(ParentAccessDialog::GetInstance(), nullptr);
 }
 
+// TODO(b/241166361) Add test to ensure PAT is communicated back to caller via
+// the the ParentAccessDialogCallback.
+
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_unittest.cc b/chrome/browser/ui/webui/extensions/extensions_internals_unittest.cc
index 4b913c933..92e156e 100644
--- a/chrome/browser/ui/webui/extensions/extensions_internals_unittest.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_internals_unittest.cc
@@ -67,9 +67,9 @@
   auto extensions_list = base::JSONReader::Read(source.WriteToString());
   ASSERT_TRUE(extensions_list) << "Failed to parse extensions internals json.";
 
-  EXPECT_EQ(extensions_list->GetListDeprecated().size(), 1U);
+  EXPECT_EQ(extensions_list->GetList().size(), 1U);
 
-  base::Value* extension_1 = &extensions_list->GetListDeprecated()[0];
+  base::Value* extension_1 = &extensions_list->GetList()[0];
   ASSERT_TRUE(extension_1->is_dict());
   base::Value* permissions = extension_1->FindDictKey("permissions");
   ASSERT_TRUE(permissions);
@@ -80,25 +80,19 @@
 
   base::Value* active = permissions->FindDictKey("active");
   ASSERT_NE(active->FindListKey("api"), nullptr);
-  EXPECT_EQ(active->FindListKey("api")->GetListDeprecated()[0].GetString(),
-            "activeTab");
+  EXPECT_EQ(active->FindListKey("api")->GetList()[0].GetString(), "activeTab");
   ASSERT_NE(active->FindListKey("manifest"), nullptr);
-  EXPECT_TRUE(active->FindListKey("manifest")
-                  ->GetListDeprecated()[0]
-                  .FindBoolKey("automation"));
+  EXPECT_TRUE(
+      active->FindListKey("manifest")->GetList()[0].FindBoolKey("automation"));
   ASSERT_NE(active->FindListKey("explicit_hosts"), nullptr);
-  EXPECT_EQ(
-      active->FindListKey("explicit_hosts")->GetListDeprecated()[0].GetString(),
-      "https://example.com/*");
+  EXPECT_EQ(active->FindListKey("explicit_hosts")->GetList()[0].GetString(),
+            "https://example.com/*");
   ASSERT_NE(active->FindListKey("scriptable_hosts"), nullptr);
-  EXPECT_EQ(active->FindListKey("scriptable_hosts")
-                ->GetListDeprecated()[0]
-                .GetString(),
+  EXPECT_EQ(active->FindListKey("scriptable_hosts")->GetList()[0].GetString(),
             "https://chromium.org/foo");
 
   base::Value* optional = permissions->FindDictKey("optional");
-  EXPECT_EQ(optional->FindListKey("api")->GetListDeprecated()[0].GetString(),
-            "storage");
+  EXPECT_EQ(optional->FindListKey("api")->GetList()[0].GetString(), "storage");
 }
 
 // Test that tab-specific permissions show up correctly in the JSON returned by
@@ -116,7 +110,7 @@
   auto extensions_list = base::JSONReader::Read(source.WriteToString());
   ASSERT_TRUE(extensions_list) << "Failed to parse extensions internals json.";
   base::Value* permissions =
-      extensions_list->GetListDeprecated()[0].FindDictKey("permissions");
+      extensions_list->GetList()[0].FindDictKey("permissions");
 
   // Check that initially there is no tab-scpecific data.
   EXPECT_EQ(permissions->FindDictKey("tab_specific")->DictSize(), 0U);
@@ -133,8 +127,7 @@
   extension->permissions_data()->UpdateTabSpecificPermissions(1,
                                                               tab_permissions);
   extensions_list = base::JSONReader::Read(source.WriteToString());
-  permissions =
-      extensions_list->GetListDeprecated()[0].FindDictKey("permissions");
+  permissions = extensions_list->GetList()[0].FindDictKey("permissions");
 
   // Check the tab specific data is present now.
   base::Value* tab_specific = permissions->FindDictKey("tab_specific");
@@ -142,17 +135,17 @@
   EXPECT_EQ(tab_specific->DictSize(), 1U);
   EXPECT_EQ(tab_specific->FindDictKey("1")
                 ->FindListKey("explicit_hosts")
-                ->GetListDeprecated()[0]
+                ->GetList()[0]
                 .GetString(),
             "https://google.com/*");
   EXPECT_EQ(tab_specific->FindDictKey("1")
                 ->FindListKey("scriptable_hosts")
-                ->GetListDeprecated()[0]
+                ->GetList()[0]
                 .GetString(),
             "https://google.com/*");
   EXPECT_EQ(tab_specific->FindDictKey("1")
                 ->FindListKey("api")
-                ->GetListDeprecated()[0]
+                ->GetList()[0]
                 .GetString(),
             "tabs");
 }
@@ -174,33 +167,33 @@
   auto extensions_list = base::JSONReader::Read(source.WriteToString());
   ASSERT_TRUE(extensions_list) << "Failed to parse extensions internals json.";
   base::Value* permissions =
-      extensions_list->GetListDeprecated()[0].FindDictKey("permissions");
+      extensions_list->GetList()[0].FindDictKey("permissions");
 
   // Check the host is initially in active hosts and there are no withheld
   // entries.
   EXPECT_EQ(permissions->FindDictKey("active")
                 ->FindListKey("explicit_hosts")
-                ->GetListDeprecated()[0]
+                ->GetList()[0]
                 .GetString(),
             "https://example.com/*");
   EXPECT_EQ(permissions->FindDictKey("withheld")
                 ->FindListKey("api")
-                ->GetListDeprecated()
+                ->GetList()
                 .size(),
             0U);
   EXPECT_EQ(permissions->FindDictKey("withheld")
                 ->FindListKey("manifest")
-                ->GetListDeprecated()
+                ->GetList()
                 .size(),
             0U);
   EXPECT_EQ(permissions->FindDictKey("withheld")
                 ->FindListKey("explicit_hosts")
-                ->GetListDeprecated()
+                ->GetList()
                 .size(),
             0U);
   EXPECT_EQ(permissions->FindDictKey("withheld")
                 ->FindListKey("scriptable_hosts")
-                ->GetListDeprecated()
+                ->GetList()
                 .size(),
             0U);
 
@@ -208,18 +201,17 @@
   extensions::ScriptingPermissionsModifier modifier(profile(), extension);
   modifier.SetWithholdHostPermissions(true);
   extensions_list = base::JSONReader::Read(source.WriteToString());
-  permissions =
-      extensions_list->GetListDeprecated()[0].FindDictKey("permissions");
+  permissions = extensions_list->GetList()[0].FindDictKey("permissions");
 
   // Check the host that was active is now withheld.
   EXPECT_EQ(permissions->FindDictKey("active")
                 ->FindListKey("explicit_hosts")
-                ->GetListDeprecated()
+                ->GetList()
                 .size(),
             0U);
   EXPECT_EQ(permissions->FindDictKey("withheld")
                 ->FindListKey("explicit_hosts")
-                ->GetListDeprecated()[0]
+                ->GetList()[0]
                 .GetString(),
             "https://example.com/*");
 }
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc
index e1d52d6..42d2d44e 100644
--- a/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -705,14 +705,16 @@
 
   auto enabled =
       GetPrefValue(prefs::kDevToolsPortForwardingEnabled)->GetIfBool();
-  const base::DictionaryValue* config;
-  if (!enabled || !GetPrefValue(prefs::kDevToolsPortForwardingConfig)
-                       ->GetAsDictionary(&config)) {
+  if (!enabled)
     return;
-  }
+
+  const base::Value::Dict* config =
+      GetPrefValue(prefs::kDevToolsPortForwardingConfig)->GetIfDict();
+  if (!config)
+    return;
 
   // Do nothing if user already took explicit action.
-  if (enabled.value() || !config->DictEmpty())
+  if (enabled.value() || !config->empty())
     return;
 
   base::DictionaryValue default_config;
diff --git a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
index 2ecc213..84b49d7 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
@@ -177,9 +177,7 @@
   expected->SetPath({prefix, "application"}, base::Value(""));
   expected->SetPath({prefix, "version"}, base::Value(""));
   expected->SetPath({prefix, "revision"}, base::Value(""));
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  expected->SetPath({prefix, "platform"}, base::Value(""));
-#else
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
   expected->SetPath({prefix, "OS"}, base::Value(""));
 #endif
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 47e46e1..72ebe05 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -17,6 +17,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/values.h"
@@ -47,6 +48,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/print_preview_resources.h"
 #include "chrome/grit/print_preview_resources_map.h"
+#include "components/device_event_log/device_event_log.h"
 #include "components/prefs/pref_service.h"
 #include "components/printing/browser/print_composite_client.h"
 #include "components/printing/browser/print_manager_utils.h"
@@ -102,6 +104,8 @@
     "Invalid page number for DidPreviewPage";
 constexpr char kInvalidPageCountForMetafileReadyForPrinting[] =
     "Invalid page count for MetafileReadyForPrinting";
+constexpr char kInvalidArgsForPrinterSettingsInvalid[] =
+    "Invalid details for PrinterSettingsInvalid";
 
 PrintPreviewUI::TestDelegate* g_test_delegate = nullptr;
 
@@ -1077,7 +1081,14 @@
 }
 
 void PrintPreviewUI::PrinterSettingsInvalid(int32_t document_cookie,
-                                            int32_t request_id) {
+                                            int32_t request_id,
+                                            const std::string& details) {
+  if (!base::IsStringASCII(details)) {
+    receiver_.ReportBadMessage(kInvalidArgsForPrinterSettingsInvalid);
+    return;
+  }
+  if (!details.empty())
+    PRINTER_LOG(ERROR) << "Printer settings invalid: " << details;
   StopWorker(document_cookie);
   if (ShouldCancelRequest(id_, request_id))
     return;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 99c4bf4..2662e73 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -70,7 +70,8 @@
   void PrintPreviewCancelled(int32_t document_cookie,
                              int32_t request_id) override;
   void PrinterSettingsInvalid(int32_t document_cookie,
-                              int32_t request_id) override;
+                              int32_t request_id,
+                              const std::string& details) override;
   void DidGetDefaultPageLayout(mojom::PageSizeMarginsPtr page_layout_in_points,
                                const gfx::Rect& printable_area_in_points,
                                bool has_custom_page_size_style,
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index e95781cf..861300c7 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -202,8 +202,9 @@
 
 base::Value::Dict GetVersionInfo() {
   base::Value::Dict version_info;
-  version_info.Set("osVersion", chromeos::version_loader::GetVersion(
-                                    chromeos::version_loader::VERSION_FULL));
+  absl::optional<std::string> version = chromeos::version_loader::GetVersion(
+      chromeos::version_loader::VERSION_FULL);
+  version_info.Set("osVersion", version.value_or("0.0.0.0"));
   version_info.Set("arcVersion", chromeos::version_loader::GetArcVersion());
   version_info.Set("osFirmware", chromeos::version_loader::GetFirmware());
   return version_info;
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
index 525a909..6f13ee0 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -272,8 +272,9 @@
   const GURL& url = gaia_urls->embedded_setup_chromeos_url(2U);
   params.Set("gaiaPath", url.path().substr(1));
 
-  params.Set("platformVersion",
-             version_loader::GetVersion(version_loader::VERSION_SHORT));
+  absl::optional<std::string> version =
+      version_loader::GetVersion(version_loader::VERSION_SHORT);
+  params.Set("platformVersion", version.value_or("0.0.0.0"));
   params.Set("constrained", "1");
   params.Set("flow", GetInlineLoginFlowName(Profile::FromWebUI(web_ui()),
                                             params.FindString("email")));
diff --git a/chrome/browser/ui/webui/version/version_handler_chromeos.cc b/chrome/browser/ui/webui/version/version_handler_chromeos.cc
index b40c7d46..77d07561 100644
--- a/chrome/browser/ui/webui/version/version_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/version/version_handler_chromeos.cc
@@ -92,8 +92,10 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-void VersionHandlerChromeOS::OnVersion(const std::string& version) {
-  FireWebUIListener("return-os-version", base::Value(version));
+void VersionHandlerChromeOS::OnVersion(
+    const absl::optional<std::string>& version) {
+  FireWebUIListener("return-os-version",
+                    base::Value(version.value_or("0.0.0.0")));
 }
 
 void VersionHandlerChromeOS::OnOSFirmware(const std::string& version) {
diff --git a/chrome/browser/ui/webui/version/version_handler_chromeos.h b/chrome/browser/ui/webui/version/version_handler_chromeos.h
index 1b75d6b..5f255ac8 100644
--- a/chrome/browser/ui/webui/version/version_handler_chromeos.h
+++ b/chrome/browser/ui/webui/version/version_handler_chromeos.h
@@ -28,7 +28,7 @@
   void RegisterMessages() override;
 
   // Callbacks from chromeos::VersionLoader.
-  void OnVersion(const std::string& version);
+  void OnVersion(const absl::optional<std::string>& version);
   void OnOSFirmware(const std::string& version);
   void OnArcAndArcAndroidSdkVersions(const std::string& version);
 
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 6f60a23..3bcf04e 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -30,8 +30,6 @@
     "commands/sub_app_install_command.h",
     "commands/web_app_command.cc",
     "commands/web_app_command.h",
-    "commands/web_app_install_command.cc",
-    "commands/web_app_install_command.h",
     "commands/web_app_uninstall_command.cc",
     "commands/web_app_uninstall_command.h",
     "daily_metrics_helper.cc",
@@ -538,10 +536,10 @@
     "commands/clear_browsing_data_command_unittest.cc",
     "commands/externally_managed_install_command_unittest.cc",
     "commands/fetch_installability_for_chrome_management_unittest.cc",
+    "commands/fetch_manifest_and_install_command_unittest.cc",
     "commands/install_from_sync_command_unittest.cc",
     "commands/install_isolated_app_command_unittest.cc",
     "commands/run_on_os_login_command_unittest.cc",
-    "commands/web_app_install_command_unittest.cc",
     "commands/web_app_uninstall_command_unittest.cc",
     "daily_metrics_helper_unittest.cc",
     "external_install_options_unittest.cc",
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
index 6d0301bf..2a0fea7 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.cc
@@ -8,23 +8,127 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/commands/web_app_command.h"
-#include "chrome/browser/web_applications/commands/web_app_install_command.h"
+#include "chrome/browser/web_applications/install_bounce_metric.h"
+#include "chrome/browser/web_applications/locks/app_lock.h"
 #include "chrome/browser/web_applications/locks/noop_lock.h"
+#include "chrome/browser/web_applications/locks/web_app_lock_manager.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_data_retriever.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_install_utils.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
+#include "components/webapps/browser/features.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
-#include "fetch_manifest_and_install_command.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/components/arc/mojom/app.mojom.h"
+#include "ash/components/arc/mojom/intent_helper.mojom.h"
+#include "ash/components/arc/session/arc_bridge_service.h"
+#include "ash/components/arc/session/arc_service_manager.h"
+#include "base/strings/string_util.h"
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/url_util.h"
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/arc.mojom.h"
+#include "chromeos/crosapi/mojom/web_app_service.mojom.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "chromeos/startup/browser_params_proxy.h"
+#endif
 
 namespace web_app {
 
+namespace {
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+constexpr bool kAddAppsToQuickLaunchBarByDefault = false;
+#else
+constexpr bool kAddAppsToQuickLaunchBarByDefault = true;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS)
+const char kChromeOsPlayPlatform[] = "chromeos_play";
+const char kPlayIntentPrefix[] =
+    "https://play.google.com/store/apps/details?id=";
+const char kPlayStorePackage[] = "com.android.vending";
+
+struct PlayStoreIntent {
+  std::string app_id;
+  std::string intent;
+};
+
+// Find the first Chrome OS app in related_applications of |manifest| and return
+// the details necessary to redirect the user to the app's listing in the Play
+// Store.
+absl::optional<PlayStoreIntent> GetPlayStoreIntentFromManifest(
+    const blink::mojom::Manifest& manifest) {
+  for (const auto& app : manifest.related_applications) {
+    std::string id = base::UTF16ToUTF8(app.id.value_or(std::u16string()));
+    if (!base::EqualsASCII(app.platform.value_or(std::u16string()),
+                           kChromeOsPlayPlatform)) {
+      continue;
+    }
+
+    if (id.empty()) {
+      // Fallback to ID in the URL.
+      if (!net::GetValueForKeyInQuery(app.url, "id", &id) || id.empty()) {
+        continue;
+      }
+    }
+
+    std::string referrer;
+    if (net::GetValueForKeyInQuery(app.url, "referrer", &referrer) &&
+        !referrer.empty()) {
+      referrer = "&referrer=" + referrer;
+    }
+
+    std::string intent = kPlayIntentPrefix + id + referrer;
+    return PlayStoreIntent{id, intent};
+  }
+  return absl::nullopt;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+bool ShouldInteractWithArc() {
+  auto* lacros_service = chromeos::LacrosService::Get();
+  return lacros_service &&
+         // Check if the feature is enabled.
+         chromeos::BrowserParamsProxy::Get()->WebAppsEnabled() &&
+         // Only use ARC installation flow if we know that remote ash-chrome is
+         // capable of installing from Play Store in lacros-chrome, to avoid
+         // redirecting users to the Play Store if they cannot install
+         // anything.
+         lacros_service->IsAvailable<crosapi::mojom::WebAppService>();
+}
+
+mojo::Remote<crosapi::mojom::Arc>* GetArcRemoteWithMinVersion(
+    uint32_t minVersion) {
+  auto* lacros_service = chromeos::LacrosService::Get();
+  if (lacros_service && lacros_service->IsAvailable<crosapi::mojom::Arc>() &&
+      lacros_service->GetInterfaceVersion(crosapi::mojom::Arc::Uuid_) >=
+          static_cast<int>(minVersion)) {
+    return &lacros_service->GetRemote<crosapi::mojom::Arc>();
+  }
+  return nullptr;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+}  // namespace
+
 FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
     WebAppInstallFinalizer* install_finalizer,
     WebAppRegistrar* registrar,
@@ -33,7 +137,7 @@
     bool bypass_service_worker_check,
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback)
-    : lock_(std::make_unique<NoopLock>()),
+    : noop_lock_(std::make_unique<NoopLock>()),
       install_finalizer_(install_finalizer),
       registrar_(registrar),
       install_surface_(install_surface),
@@ -41,7 +145,9 @@
       bypass_service_worker_check_(bypass_service_worker_check),
       dialog_callback_(std::move(dialog_callback)),
       install_callback_(std::move(callback)),
-      data_retriever_(std::make_unique<WebAppDataRetriever>()) {}
+      data_retriever_(std::make_unique<WebAppDataRetriever>()),
+      install_error_log_entry_(/*background_installation=*/false,
+                               install_surface_) {}
 
 FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
     WebAppInstallFinalizer* install_finalizer,
@@ -53,7 +159,7 @@
     OnceInstallCallback callback,
     bool use_fallback,
     WebAppInstallFlow flow)
-    : lock_(std::make_unique<NoopLock>()),
+    : noop_lock_(std::make_unique<NoopLock>()),
       install_finalizer_(install_finalizer),
       registrar_(registrar),
       install_surface_(install_surface),
@@ -63,35 +169,73 @@
       install_callback_(std::move(callback)),
       data_retriever_(std::make_unique<WebAppDataRetriever>()),
       use_fallback_(use_fallback),
-      flow_(flow) {}
+      flow_(flow),
+      install_error_log_entry_(/*background_installation=*/false,
+                               install_surface_) {}
+
+FetchManifestAndInstallCommand::FetchManifestAndInstallCommand(
+    WebAppInstallFinalizer* install_finalizer,
+    WebAppRegistrar* registrar,
+    webapps::WebappInstallSource install_surface,
+    base::WeakPtr<content::WebContents> contents,
+    bool bypass_service_worker_check,
+    WebAppInstallDialogCallback dialog_callback,
+    OnceInstallCallback callback,
+    bool use_fallback,
+    WebAppInstallFlow flow,
+    std::unique_ptr<WebAppDataRetriever> data_retriever)
+    : noop_lock_(std::make_unique<NoopLock>()),
+      install_finalizer_(install_finalizer),
+      registrar_(registrar),
+      install_surface_(install_surface),
+      web_contents_(contents),
+      bypass_service_worker_check_(bypass_service_worker_check),
+      dialog_callback_(std::move(dialog_callback)),
+      install_callback_(std::move(callback)),
+      data_retriever_(std::move(data_retriever)),
+      use_fallback_(use_fallback),
+      flow_(flow),
+      install_error_log_entry_(/*background_installation=*/false,
+                               install_surface_) {}
 
 FetchManifestAndInstallCommand::~FetchManifestAndInstallCommand() = default;
 
 Lock& FetchManifestAndInstallCommand::lock() const {
-  return *lock_;
+  DCHECK(noop_lock_ || app_lock_);
+
+  if (noop_lock_ != nullptr)
+    return *noop_lock_;
+
+  return *app_lock_;
 }
 
 void FetchManifestAndInstallCommand::Start() {
+  // This metric is recorded regardless of the installation result.
+  if (webapps::InstallableMetrics::IsReportableInstallSource(
+          install_surface_)) {
+    webapps::InstallableMetrics::TrackInstallEvent(install_surface_);
+  }
+
   if (IsWebContentsDestroyed()) {
     Abort(webapps::InstallResultCode::kWebContentsDestroyed);
     return;
   }
 
+  DCHECK(AreWebAppsUserInstallable(
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext())));
+
   if (use_fallback_) {
     data_retriever_->GetWebAppInstallInfo(
         web_contents_.get(),
         base::BindOnce(&FetchManifestAndInstallCommand::OnGetWebAppInstallInfo,
-                       weak_factory_.GetWeakPtr()));
+                       weak_ptr_factory_.GetWeakPtr()));
   } else {
-    install_info_ = std::make_unique<WebAppInstallInfo>();
+    web_app_info_ = std::make_unique<WebAppInstallInfo>();
     FetchManifest();
   }
 }
 
-void FetchManifestAndInstallCommand::OnSyncSourceRemoved() {
-  // TODO(crbug.com/1320086): remove after uninstall from sync is async.
-  Abort(webapps::InstallResultCode::kAppNotInRegistrarAfterCommit);
-}
+void FetchManifestAndInstallCommand::OnSyncSourceRemoved() {}
 
 void FetchManifestAndInstallCommand::OnShutdown() {
   Abort(webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
@@ -105,7 +249,6 @@
 base::Value FetchManifestAndInstallCommand::ToDebugValue() const {
   auto debug_value = debug_log_.Clone();
   debug_value.Set("command_name", "FetchManifestAndInstallCommand");
-  debug_value.Set("command_id", id());
   debug_value.Set("app_id", app_id_);
   return base::Value(std::move(debug_value));
 }
@@ -136,10 +279,10 @@
     return;
   }
 
-  install_info_ = std::move(fallback_web_app_info);
+  web_app_info_ = std::move(fallback_web_app_info);
   LogInstallInfo();
 
-  return FetchManifest();
+  FetchManifest();
 }
 
 void FetchManifestAndInstallCommand::FetchManifest() {
@@ -152,7 +295,7 @@
       web_contents_.get(), bypass_service_worker_check_,
       base::BindOnce(
           &FetchManifestAndInstallCommand::OnDidPerformInstallableCheck,
-          weak_factory_.GetWeakPtr()));
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
 void FetchManifestAndInstallCommand::OnDidPerformInstallableCheck(
@@ -171,38 +314,269 @@
     Abort(webapps::InstallResultCode::kNotValidManifestForWebApp);
     return;
   }
-
-  auto manifest_id = install_info_->manifest_id;
-  auto start_url = install_info_->start_url;
-
   if (opt_manifest) {
-    if (opt_manifest->start_url.is_valid())
-      install_info_->start_url = opt_manifest->start_url;
-
-    if (opt_manifest->id.has_value()) {
-      install_info_->manifest_id = absl::optional<std::string>(
-          base::UTF16ToUTF8(opt_manifest->id.value()));
-    }
+    UpdateWebAppInfoFromManifest(*opt_manifest, manifest_url,
+                                 web_app_info_.get());
     LogInstallInfo();
   }
 
-  app_id_ = GenerateAppId(install_info_->manifest_id, install_info_->start_url);
+  if (flow_ == WebAppInstallFlow::kCreateShortcut &&
+      base::FeatureList::IsEnabled(
+          webapps::features::kCreateShortcutIgnoresManifest)) {
+    // When creating a shortcut, the |manifest_id| is not part of the App's
+    // primary key. The only thing that identifies a shortcut is the start URL,
+    // which is always set to the current page.
+    *web_app_info_ = WebAppInstallInfo::CreateInstallInfoForCreateShortcut(
+        web_contents_->GetLastCommittedURL(), *web_app_info_);
+  }
 
-  command_manager()->ScheduleCommand(std::make_unique<WebAppInstallCommand>(
-      app_id_, install_surface_, std::move(install_info_),
-      std::move(opt_manifest), manifest_url, flow_, std::move(dialog_callback_),
-      std::move(install_callback_),
-      Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
-      install_finalizer_, std::move(data_retriever_), web_contents_));
-  SignalCompletionAndSelfDestruct(CommandResult::kSuccess, base::DoNothing());
+  base::flat_set<GURL> icon_urls = GetValidIconUrlsToDownload(*web_app_info_);
+
+  opt_manifest_ = std::move(opt_manifest);
+
+  // If the manifest specified icons, don't use the page icons.
+  const bool skip_page_favicons =
+      opt_manifest_ && !opt_manifest_->icons.empty();
+
+  app_id_ = GenerateAppId(web_app_info_->manifest_id, web_app_info_->start_url);
+
+  app_lock_ = command_manager()->lock_manager().UpgradeAndAcquireLock(
+      std::move(noop_lock_), {app_id_},
+      base::BindOnce(
+          &FetchManifestAndInstallCommand::CheckForPlayStoreIntentOrGetIcons,
+          weak_ptr_factory_.GetWeakPtr(), std::move(icon_urls),
+          skip_page_favicons));
+}
+
+void FetchManifestAndInstallCommand::CheckForPlayStoreIntentOrGetIcons(
+    base::flat_set<GURL> icon_urls,
+    bool skip_page_favicons) {
+  bool is_create_shortcut = flow_ == WebAppInstallFlow::kCreateShortcut;
+  // Background installations are not a user-triggered installs, and thus
+  // cannot be sent to the store.
+  bool skip_store = is_create_shortcut || !opt_manifest_;
+
+  if (!skip_store) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    absl::optional<PlayStoreIntent> intent =
+        GetPlayStoreIntentFromManifest(*opt_manifest_);
+    if (intent) {
+      auto* arc_service_manager = arc::ArcServiceManager::Get();
+      if (arc_service_manager) {
+        auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+            arc_service_manager->arc_bridge_service()->app(), IsInstallable);
+        if (instance) {
+          instance->IsInstallable(
+              intent->app_id,
+              base::BindOnce(&FetchManifestAndInstallCommand::
+                                 OnDidCheckForIntentToPlayStore,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             std::move(icon_urls), skip_page_favicons,
+                             intent->intent));
+          return;
+        }
+      }
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    if (ShouldInteractWithArc()) {
+      absl::optional<PlayStoreIntent> intent =
+          GetPlayStoreIntentFromManifest(*opt_manifest_);
+      mojo::Remote<crosapi::mojom::Arc>* opt_arc = GetArcRemoteWithMinVersion(
+          crosapi::mojom::Arc::MethodMinVersions::kIsInstallableMinVersion);
+      if (opt_arc && intent) {
+        mojo::Remote<crosapi::mojom::Arc>& arc = *opt_arc;
+        arc->IsInstallable(
+            intent->app_id,
+            base::BindOnce(&FetchManifestAndInstallCommand::
+                               OnDidCheckForIntentToPlayStoreLacros,
+                           weak_ptr_factory_.GetWeakPtr(), std::move(icon_urls),
+                           skip_page_favicons, intent->intent));
+        return;
+      }
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  }
+  OnDidCheckForIntentToPlayStore(std::move(icon_urls), skip_page_favicons,
+                                 /*intent=*/"",
+                                 /*should_intent_to_store=*/false);
+}
+
+void FetchManifestAndInstallCommand::OnDidCheckForIntentToPlayStore(
+    base::flat_set<GURL> icon_urls,
+    bool skip_page_favicons,
+    const std::string& intent,
+    bool should_intent_to_store) {
+  if (IsWebContentsDestroyed()) {
+    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
+    return;
+  }
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (should_intent_to_store && !intent.empty()) {
+    auto* arc_service_manager = arc::ArcServiceManager::Get();
+    if (arc_service_manager) {
+      auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
+          arc_service_manager->arc_bridge_service()->intent_helper(),
+          HandleUrl);
+      if (instance) {
+        instance->HandleUrl(intent, kPlayStorePackage);
+        Abort(webapps::InstallResultCode::kIntentToPlayStore);
+        return;
+      }
+    }
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (ShouldInteractWithArc() && should_intent_to_store && !intent.empty()) {
+    mojo::Remote<crosapi::mojom::Arc>* opt_arc = GetArcRemoteWithMinVersion(
+        crosapi::mojom::Arc::MethodMinVersions::kHandleUrlMinVersion);
+    if (opt_arc) {
+      mojo::Remote<crosapi::mojom::Arc>& arc = *opt_arc;
+      arc->HandleUrl(intent, kPlayStorePackage);
+      Abort(webapps::InstallResultCode::kIntentToPlayStore);
+      return;
+    }
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  data_retriever_->GetIcons(
+      web_contents_.get(), std::move(icon_urls), skip_page_favicons,
+      base::BindOnce(
+          &FetchManifestAndInstallCommand::OnIconsRetrievedShowDialog,
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+void FetchManifestAndInstallCommand::OnDidCheckForIntentToPlayStoreLacros(
+    base::flat_set<GURL> icon_urls,
+    bool skip_page_favicons,
+    const std::string& intent,
+    crosapi::mojom::IsInstallableResult result) {
+  OnDidCheckForIntentToPlayStore(
+      std::move(icon_urls), skip_page_favicons, intent,
+      result == crosapi::mojom::IsInstallableResult::kInstallable);
+}
+#endif
+
+void FetchManifestAndInstallCommand::OnIconsRetrievedShowDialog(
+    IconsDownloadedResult result,
+    IconsMap icons_map,
+    DownloadedIconsHttpResults icons_http_results) {
+  if (IsWebContentsDestroyed()) {
+    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
+    return;
+  }
+
+  DCHECK(web_app_info_);
+
+  PopulateProductIcons(web_app_info_.get(), &icons_map);
+  PopulateOtherIcons(web_app_info_.get(), icons_map);
+
+  RecordDownloadedIconsResultAndHttpStatusCodes(result, icons_http_results);
+  install_error_log_entry_.LogDownloadedIconsErrors(
+      *web_app_info_, result, icons_map, icons_http_results);
+
+  if (!dialog_callback_) {
+    OnDialogCompleted(/*user_accepted=*/true, std::move(web_app_info_));
+  } else {
+    std::move(dialog_callback_)
+        .Run(web_contents_.get(), std::move(web_app_info_),
+             base::BindOnce(&FetchManifestAndInstallCommand::OnDialogCompleted,
+                            weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void FetchManifestAndInstallCommand::OnDialogCompleted(
+    bool user_accepted,
+    std::unique_ptr<WebAppInstallInfo> web_app_info) {
+  if (IsWebContentsDestroyed()) {
+    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
+    return;
+  }
+
+  if (!user_accepted) {
+    Abort(webapps::InstallResultCode::kUserInstallDeclined);
+    return;
+  }
+
+  web_app_info_ = std::move(web_app_info);
+
+  WebAppInstallFinalizer::FinalizeOptions finalize_options(install_surface_);
+
+  finalize_options.locally_installed = true;
+  finalize_options.overwrite_existing_manifest_fields = true;
+  finalize_options.add_to_applications_menu = true;
+  finalize_options.add_to_desktop = true;
+  finalize_options.add_to_quick_launch_bar = kAddAppsToQuickLaunchBarByDefault;
+
+  install_finalizer_->FinalizeInstall(
+      *web_app_info_, finalize_options,
+      base::BindOnce(
+          &FetchManifestAndInstallCommand::OnInstallFinalizedMaybeReparentTab,
+          weak_ptr_factory_.GetWeakPtr()));
+
+  // Check that the finalizer hasn't called OnInstallFinalizedMaybeReparentTab
+  // synchronously:
+  DCHECK(install_callback_);
+}
+
+void FetchManifestAndInstallCommand::OnInstallFinalizedMaybeReparentTab(
+    const AppId& app_id,
+    webapps::InstallResultCode code,
+    OsHooksErrors os_hooks_errors) {
+  if (IsWebContentsDestroyed()) {
+    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
+    return;
+  }
+
+  if (code != webapps::InstallResultCode::kSuccessNewInstall) {
+    Abort(code);
+    return;
+  }
+
+  RecordWebAppInstallationTimestamp(
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext())
+          ->GetPrefs(),
+      app_id, install_surface_);
+
+  RecordAppBanner(web_contents_.get(), web_app_info_->start_url);
+
+  bool error = os_hooks_errors[OsHookType::kShortcuts];
+  const bool can_reparent_tab =
+      install_finalizer_->CanReparentTab(app_id, !error);
+
+  if (can_reparent_tab &&
+      (web_app_info_->user_display_mode != UserDisplayMode::kBrowser)) {
+    install_finalizer_->ReparentTab(app_id, !error, web_contents_.get());
+  }
+
+  OnInstallCompleted(app_id, webapps::InstallResultCode::kSuccessNewInstall);
+}
+
+void FetchManifestAndInstallCommand::OnInstallCompleted(
+    const AppId& app_id,
+    webapps::InstallResultCode code) {
+  if (base::FeatureList::IsEnabled(features::kRecordWebAppDebugInfo)) {
+    base::Value task_error_dict = install_error_log_entry_.TakeErrorDict();
+    if (!task_error_dict.DictEmpty())
+      command_manager()->LogToInstallManager(std::move(task_error_dict));
+  }
+
+  webapps::InstallableMetrics::TrackInstallResult(webapps::IsSuccess(code));
+  SignalCompletionAndSelfDestruct(
+      webapps::IsSuccess(code) ? CommandResult::kSuccess
+                               : CommandResult::kFailure,
+      base::BindOnce(std::move(install_callback_), app_id, code));
 }
 
 void FetchManifestAndInstallCommand::LogInstallInfo() {
   debug_log_.Set("manifest_id",
-                 install_info_->manifest_id.has_value()
-                     ? base::Value(install_info_->manifest_id.value())
+                 web_app_info_->manifest_id.has_value()
+                     ? base::Value(web_app_info_->manifest_id.value())
                      : base::Value());
-  debug_log_.Set("start_url", install_info_->start_url.spec());
-  debug_log_.Set("name", install_info_->title);
+  debug_log_.Set("start_url", web_app_info_->start_url.spec());
+  debug_log_.Set("name", web_app_info_->title);
 }
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
index e23b79a1..84937ff 100644
--- a/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h
@@ -13,16 +13,22 @@
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_params.h"
+#include "chrome/browser/web_applications/web_app_logging.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/arc.mojom.h"
+#endif
+
 namespace content {
 class WebContents;
 }
 
 namespace web_app {
 
+class AppLock;
 class NoopLock;
 class WebAppDataRetriever;
 class WebAppInstallFinalizer;
@@ -58,6 +64,21 @@
                                  bool use_fallback,
                                  WebAppInstallFlow flow);
 
+  // TODO(https://crbug.com/1364390): Remove the constructors above and fix the
+  // param orders here after replacing callsites to use provider()->scheduler()
+  // interface.
+  FetchManifestAndInstallCommand(
+      WebAppInstallFinalizer* install_finalizer,
+      WebAppRegistrar* registrar,
+      webapps::WebappInstallSource install_surface,
+      base::WeakPtr<content::WebContents> contents,
+      bool bypass_service_worker_check,
+      WebAppInstallDialogCallback dialog_callback,
+      OnceInstallCallback callback,
+      bool use_fallback,
+      WebAppInstallFlow flow,
+      std::unique_ptr<WebAppDataRetriever> data_retriever);
+
   ~FetchManifestAndInstallCommand() override;
 
   Lock& lock() const override;
@@ -82,9 +103,48 @@
                                     const GURL& manifest_url,
                                     bool valid_manifest_for_web_app,
                                     bool is_installable);
+
+  // Either dispatches an asynchronous check for whether this installation
+  // should be stopped and an intent to the Play Store should be made, or
+  // synchronously calls OnDidCheckForIntentToPlayStore() implicitly failing the
+  // check if it cannot be made.
+  void CheckForPlayStoreIntentOrGetIcons(base::flat_set<GURL> icon_urls,
+                                         bool skip_page_favicons);
+
+  // Called when the asynchronous check for whether an intent to the Play Store
+  // should be made returns.
+  void OnDidCheckForIntentToPlayStore(base::flat_set<GURL> icon_urls,
+                                      bool skip_page_favicons,
+                                      const std::string& intent,
+                                      bool should_intent_to_store);
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Called when the asynchronous check for whether an intent to the Play Store
+  // should be made returns (Lacros adapter that calls
+  // |OnDidCheckForIntentToPlayStore| based on |result|).
+  void OnDidCheckForIntentToPlayStoreLacros(
+      base::flat_set<GURL> icon_urls,
+      bool skip_page_favicons,
+      const std::string& intent,
+      crosapi::mojom::IsInstallableResult result);
+#endif
+
+  void OnIconsRetrievedShowDialog(
+      IconsDownloadedResult result,
+      IconsMap icons_map,
+      DownloadedIconsHttpResults icons_http_results);
+  void OnDialogCompleted(bool user_accepted,
+                         std::unique_ptr<WebAppInstallInfo> web_app_info);
+  void OnInstallFinalizedMaybeReparentTab(const AppId& app_id,
+                                          webapps::InstallResultCode code,
+                                          OsHooksErrors os_hooks_errors);
+
+  void OnInstallCompleted(const AppId& app_id, webapps::InstallResultCode code);
+
   void LogInstallInfo();
 
-  std::unique_ptr<NoopLock> lock_;
+  std::unique_ptr<NoopLock> noop_lock_;
+  std::unique_ptr<AppLock> app_lock_;
 
   raw_ptr<WebAppInstallFinalizer> install_finalizer_;
   raw_ptr<WebAppRegistrar> registrar_;
@@ -96,7 +156,10 @@
   OnceInstallCallback install_callback_;
 
   std::unique_ptr<WebAppDataRetriever> data_retriever_;
-  std::unique_ptr<WebAppInstallInfo> install_info_;
+
+  AppId app_id_;
+  std::unique_ptr<WebAppInstallInfo> web_app_info_;
+  blink::mojom::ManifestPtr opt_manifest_;
 
   base::Value::Dict debug_log_;
 
@@ -104,9 +167,9 @@
   bool use_fallback_ = false;
   WebAppInstallFlow flow_{};
 
-  AppId app_id_{};
+  InstallErrorLogEntry install_error_log_entry_;
 
-  base::WeakPtrFactory<FetchManifestAndInstallCommand> weak_factory_{this};
+  base::WeakPtrFactory<FetchManifestAndInstallCommand> weak_ptr_factory_{this};
 };
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/commands/web_app_install_command_unittest.cc b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
similarity index 76%
rename from chrome/browser/web_applications/commands/web_app_install_command_unittest.cc
rename to chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
index 7f53cccf..9da0b3a 100644
--- a/chrome/browser/web_applications/commands/web_app_install_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/fetch_manifest_and_install_command_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/web_applications/commands/web_app_install_command.h"
+#include "chrome/browser/web_applications/commands/fetch_manifest_and_install_command.h"
 #include <memory>
 
 #include "base/run_loop.h"
@@ -44,7 +44,7 @@
 namespace web_app {
 namespace {
 
-class WebAppInstallCommandTest : public WebAppTest {
+class FetchManifestAndInstallCommandTest : public WebAppTest {
  public:
   const GURL kWebAppUrl = GURL("https://example.com/path/index.html");
   const AppId kWebAppId =
@@ -135,10 +135,11 @@
     return manifest;
   }
 
-  std::unique_ptr<WebAppDataRetriever> SetupFakeDataRetriever(
+  std::unique_ptr<FakeDataRetriever> SetupFakeDataRetriever(
       IconsMap icons_map,
       IconsDownloadedResult result,
-      int http_status_codes) {
+      int http_status_codes,
+      blink::mojom::ManifestPtr opt_manifest = blink::mojom::ManifestPtr()) {
     auto data_retriever = std::make_unique<FakeDataRetriever>();
 
     data_retriever->SetIconsDownloadedResult(result);
@@ -151,6 +152,22 @@
 
     // Moves `icons_map` last.
     data_retriever->SetIcons(std::move(icons_map));
+
+    data_retriever->SetManifest(
+        opt_manifest ? std::move(opt_manifest) : CreateValidManifest(),
+        /*is_installable=*/true);
+    data_retriever->SetEmptyRendererWebAppInstallInfo();
+    return data_retriever;
+  }
+
+  std::unique_ptr<FakeDataRetriever> SetupDefaultFakeDataRetriever(
+      blink::mojom::ManifestPtr opt_manifest = blink::mojom::ManifestPtr()) {
+    auto data_retriever = std::make_unique<FakeDataRetriever>();
+
+    data_retriever->SetManifest(
+        opt_manifest ? std::move(opt_manifest) : CreateValidManifest(),
+        /*is_installable=*/true);
+    data_retriever->SetEmptyRendererWebAppInstallInfo();
     return data_retriever;
   }
 
@@ -159,24 +176,22 @@
       std::unique_ptr<WebAppDataRetriever> data_retriever,
       webapps::WebappInstallSource install_surface,
       WebAppInstallDialogCallback dialog_callback,
-      std::unique_ptr<WebAppInstallInfo> web_app_info,
-      blink::mojom::ManifestPtr opt_manifest,
-      const GURL& manifest_url,
-      WebAppInstallFlow flow) {
+      WebAppInstallFlow flow,
+      bool use_fallback = false) {
     webapps::InstallResultCode result;
     base::RunLoop run_loop;
     provider()->command_manager().ScheduleCommand(
-        std::make_unique<WebAppInstallCommand>(
-            app_id, install_surface, std::move(web_app_info),
-            std::move(opt_manifest), manifest_url, flow,
-            std::move(dialog_callback),
+        std::make_unique<FetchManifestAndInstallCommand>(
+            &provider()->install_finalizer(), &provider()->registrar(),
+            webapps::WebappInstallSource::MENU_BROWSER_TAB,
+            web_contents()->GetWeakPtr(),
+            /*bypass_service_worker_check=*/false, std::move(dialog_callback),
             base::BindLambdaForTesting(
                 [&](const AppId& id, webapps::InstallResultCode code) {
                   result = code;
                   run_loop.Quit();
                 }),
-            profile(), &provider()->install_finalizer(),
-            std::move(data_retriever), web_contents()->GetWeakPtr()));
+            use_fallback, flow, std::move(data_retriever)));
     run_loop.Run();
     return result;
   }
@@ -193,42 +208,73 @@
 #endif
 };
 
-TEST_F(WebAppInstallCommandTest, SuccessWithManifest) {
-  EXPECT_EQ(InstallAndWait(
-                kWebAppId, std::make_unique<FakeDataRetriever>(),
-                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true, UserDisplayMode::kStandalone),
-                std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-                kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-            webapps::InstallResultCode::kSuccessNewInstall);
+TEST_F(FetchManifestAndInstallCommandTest, SuccessWithManifest) {
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(true, UserDisplayMode::kStandalone),
+                     WebAppInstallFlow::kInstallSite),
+      webapps::InstallResultCode::kSuccessNewInstall);
   EXPECT_TRUE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(1, fake_ui_manager()->num_reparent_tab_calls());
 }
 
-TEST_F(WebAppInstallCommandTest, SuccessWithoutReparent) {
-  EXPECT_EQ(InstallAndWait(
-                kWebAppId, std::make_unique<FakeDataRetriever>(),
-                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true, UserDisplayMode::kBrowser),
-                std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-                kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-            webapps::InstallResultCode::kSuccessNewInstall);
-  EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
+TEST_F(FetchManifestAndInstallCommandTest, SuccessWithFallbackInstall) {
+  auto data_retriever = SetupDefaultFakeDataRetriever();
+
+  auto web_app_info = std::make_unique<WebAppInstallInfo>();
+
+  web_app_info->start_url = kWebAppUrl;
+  web_app_info->title = u"test app";
+  web_app_info->scope = kWebAppUrl;
+  web_app_info->user_display_mode = UserDisplayMode::kBrowser;
+  data_retriever->SetRendererWebAppInstallInfo(std::move(web_app_info));
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId, std::move(data_retriever),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(true, UserDisplayMode::kStandalone),
+                     WebAppInstallFlow::kInstallSite, /*use_fallback=*/true),
+      webapps::InstallResultCode::kSuccessNewInstall);
+  EXPECT_TRUE(provider()->registrar().IsLocallyInstalled(kWebAppId));
+  EXPECT_EQ(1, fake_ui_manager()->num_reparent_tab_calls());
 }
 
-TEST_F(WebAppInstallCommandTest, UserInstallDeclined) {
-  EXPECT_EQ(InstallAndWait(
-                kWebAppId, std::make_unique<FakeDataRetriever>(),
-                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(false, UserDisplayMode::kStandalone),
-                std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-                kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-            webapps::InstallResultCode::kUserInstallDeclined);
+TEST_F(FetchManifestAndInstallCommandTest,
+       FallbackInstallWithFailToGetInstallInfo) {
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId, std::make_unique<FakeDataRetriever>(),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(true, UserDisplayMode::kStandalone),
+                     WebAppInstallFlow::kInstallSite, /*use_fallback=*/true),
+      webapps::InstallResultCode::kGetWebAppInstallInfoFailed);
   EXPECT_FALSE(provider()->registrar().IsLocallyInstalled(kWebAppId));
   EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
 }
 
-TEST_F(WebAppInstallCommandTest, Shutdown) {
+TEST_F(FetchManifestAndInstallCommandTest, SuccessWithoutReparent) {
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(true, UserDisplayMode::kBrowser),
+
+                     WebAppInstallFlow::kInstallSite),
+      webapps::InstallResultCode::kSuccessNewInstall);
+  EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
+}
+
+TEST_F(FetchManifestAndInstallCommandTest, UserInstallDeclined) {
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId, SetupDefaultFakeDataRetriever(),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(false, UserDisplayMode::kStandalone),
+
+                     WebAppInstallFlow::kInstallSite),
+      webapps::InstallResultCode::kUserInstallDeclined);
+  EXPECT_FALSE(provider()->registrar().IsLocallyInstalled(kWebAppId));
+  EXPECT_EQ(0, fake_ui_manager()->num_reparent_tab_calls());
+}
+
+TEST_F(FetchManifestAndInstallCommandTest, Shutdown) {
   webapps::InstallResultCode result;
   bool result_populated = false;
 
@@ -242,18 +288,18 @@
       });
 
   provider()->command_manager().ScheduleCommand(
-      std::make_unique<WebAppInstallCommand>(
-          kWebAppId, webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-          std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-          kWebAppManifestUrl, WebAppInstallFlow::kInstallSite,
-          std::move(dialog_callback),
+      std::make_unique<FetchManifestAndInstallCommand>(
+          &provider()->install_finalizer(), &provider()->registrar(),
+          webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+          web_contents()->GetWeakPtr(),
+          /*bypass_service_worker_check=*/false, std::move(dialog_callback),
           base::BindLambdaForTesting(
               [&](const AppId& id, webapps::InstallResultCode code) {
                 result_populated = true;
                 result = code;
               }),
-          profile(), &provider()->install_finalizer(),
-          std::make_unique<FakeDataRetriever>(), web_contents()->GetWeakPtr()));
+          /*use_fallback=*/false, WebAppInstallFlow::kInstallSite,
+          SetupDefaultFakeDataRetriever()));
 
   dialog_runloop.Run();
   provider()->command_manager().Shutdown();
@@ -263,25 +309,25 @@
             webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
 }
 
-TEST_F(WebAppInstallCommandTest, WebContentsDestroyed) {
+TEST_F(FetchManifestAndInstallCommandTest, WebContentsDestroyed) {
   webapps::InstallResultCode result;
   bool result_populated = false;
 
   base::RunLoop loop;
   provider()->command_manager().ScheduleCommand(
-      std::make_unique<WebAppInstallCommand>(
-          kWebAppId, webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-          std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-          kWebAppManifestUrl, WebAppInstallFlow::kInstallSite,
-          CreateDialogCallback(),
+      std::make_unique<FetchManifestAndInstallCommand>(
+          &provider()->install_finalizer(), &provider()->registrar(),
+          webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+          web_contents()->GetWeakPtr(),
+          /*bypass_service_worker_check=*/false, CreateDialogCallback(),
           base::BindLambdaForTesting(
               [&](const AppId& id, webapps::InstallResultCode code) {
                 result_populated = true;
                 result = code;
                 loop.Quit();
               }),
-          profile(), &provider()->install_finalizer(),
-          std::make_unique<FakeDataRetriever>(), web_contents()->GetWeakPtr()));
+          /*use_fallback=*/false, WebAppInstallFlow::kInstallSite,
+          SetupDefaultFakeDataRetriever()));
 
   DeleteContents();
   loop.Run();
@@ -290,7 +336,7 @@
   EXPECT_EQ(result, webapps::InstallResultCode::kWebContentsDestroyed);
 }
 
-TEST_F(WebAppInstallCommandTest, WriteDataToDisk) {
+TEST_F(FetchManifestAndInstallCommandTest, WriteDataToDisk) {
   struct TestIconInfo {
     IconPurpose purpose;
     std::string icon_url_name;
@@ -348,16 +394,14 @@
       GetManifestResourcesDirectory(web_apps_dir);
   EXPECT_FALSE(file_utils().DirectoryExists(manifest_resources_directory));
 
-  EXPECT_EQ(
-      InstallAndWait(kWebAppId,
-                     SetupFakeDataRetriever(std::move(icons_map),
-                                            IconsDownloadedResult::kCompleted,
-                                            net::HttpStatusCode::HTTP_OK),
-                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(true),
-                     std::make_unique<WebAppInstallInfo>(), std::move(manifest),
-                     kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-      webapps::InstallResultCode::kSuccessNewInstall);
+  EXPECT_EQ(InstallAndWait(
+                kWebAppId,
+                SetupFakeDataRetriever(
+                    std::move(icons_map), IconsDownloadedResult::kCompleted,
+                    net::HttpStatusCode::HTTP_OK, std::move(manifest)),
+                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
+            webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
 
@@ -419,7 +463,7 @@
       net::HttpStatusCode::HTTP_OK, 1);
 }
 
-TEST_F(WebAppInstallCommandTest, GetIcons_PrimaryPageChanged) {
+TEST_F(FetchManifestAndInstallCommandTest, GetIcons_PrimaryPageChanged) {
   const base::FilePath web_apps_dir = GetWebAppsRootDirectory(profile());
   const base::FilePath manifest_resources_directory =
       GetManifestResourcesDirectory(web_apps_dir);
@@ -433,9 +477,7 @@
                                  IconsDownloadedResult::kPrimaryPageChanged,
                                  net::HttpStatusCode::HTTP_OK),
           webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-          CreateDialogCallback(true), std::make_unique<WebAppInstallInfo>(),
-          CreateValidManifest(), kWebAppManifestUrl,
-          WebAppInstallFlow::kInstallSite),
+          CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
       webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
@@ -480,7 +522,7 @@
       "WebApp.Icon.DownloadedHttpStatusCodeOnSync", 0);
 }
 
-TEST_F(WebAppInstallCommandTest, GetIcons_IconNotFound) {
+TEST_F(FetchManifestAndInstallCommandTest, GetIcons_IconNotFound) {
   const base::FilePath web_apps_dir = GetWebAppsRootDirectory(profile());
   const base::FilePath manifest_resources_directory =
       GetManifestResourcesDirectory(web_apps_dir);
@@ -496,8 +538,8 @@
                                        net::HttpStatusCode::HTTP_NOT_FOUND),
                 webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
                 CreateDialogCallback(true),
-                std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-                kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
+
+                WebAppInstallFlow::kInstallSite),
             webapps::InstallResultCode::kSuccessNewInstall);
 
   EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));
@@ -537,7 +579,7 @@
       "WebApp.Icon.DownloadedHttpStatusCodeOnSync", 0);
 }
 
-TEST_F(WebAppInstallCommandTest, WriteDataToDiskFailed) {
+TEST_F(FetchManifestAndInstallCommandTest, WriteDataToDiskFailed) {
   IconsMap icons_map;
   AddIconToIconsMap(GURL("https://example.com/app.ico"), icon_size::k512,
                     SK_ColorBLUE, &icons_map);
@@ -551,16 +593,16 @@
   // Induce an error: Simulate "Disk Full" for writing icon files.
   file_utils().SetRemainingDiskSpaceSize(1024);
 
-  EXPECT_EQ(InstallAndWait(
-                kWebAppId,
-                SetupFakeDataRetriever(std::move(icons_map),
-                                       IconsDownloadedResult::kCompleted,
-                                       net::HttpStatusCode::HTTP_OK),
-                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                CreateDialogCallback(true),
-                std::make_unique<WebAppInstallInfo>(), CreateValidManifest(),
-                kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-            webapps::InstallResultCode::kWriteDataFailed);
+  EXPECT_EQ(
+      InstallAndWait(kWebAppId,
+                     SetupFakeDataRetriever(std::move(icons_map),
+                                            IconsDownloadedResult::kCompleted,
+                                            net::HttpStatusCode::HTTP_OK),
+                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                     CreateDialogCallback(true),
+
+                     WebAppInstallFlow::kInstallSite),
+      webapps::InstallResultCode::kWriteDataFailed);
 
   const base::FilePath temp_dir = web_apps_dir.AppendASCII("Temp");
   EXPECT_TRUE(file_utils().DirectoryExists(temp_dir));
@@ -572,7 +614,7 @@
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(WebAppInstallCommandTest, IntentToPlayStore) {
+TEST_F(FetchManifestAndInstallCommandTest, IntentToPlayStore) {
   arc_test().app_instance()->set_is_installable(true);
 
   auto manifest = CreateValidManifest();
@@ -581,13 +623,11 @@
   related_app.id = u"com.app.id";
   manifest->related_applications.push_back(std::move(related_app));
 
-  EXPECT_EQ(
-      InstallAndWait(kWebAppId, std::make_unique<FakeDataRetriever>(),
-                     webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
-                     CreateDialogCallback(true),
-                     std::make_unique<WebAppInstallInfo>(), std::move(manifest),
-                     kWebAppManifestUrl, WebAppInstallFlow::kInstallSite),
-      webapps::InstallResultCode::kIntentToPlayStore);
+  EXPECT_EQ(InstallAndWait(
+                kWebAppId, SetupDefaultFakeDataRetriever(std::move(manifest)),
+                webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+                CreateDialogCallback(true), WebAppInstallFlow::kInstallSite),
+            webapps::InstallResultCode::kIntentToPlayStore);
 }
 #endif
 
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.cc b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
index 05266b54..9c192b7 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command.cc
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
@@ -14,6 +14,7 @@
 #include "base/callback_helpers.h"
 #include "base/check.h"
 #include "base/containers/flat_set.h"
+#include "base/memory/ptr_util.h"
 #include "base/sequence_checker.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
@@ -57,6 +58,23 @@
 
 }  // namespace
 
+base::expected<std::unique_ptr<InstallIsolatedAppCommand>,
+               InstallIsolatedAppCommandCreateError>
+InstallIsolatedAppCommand::Create(
+    const GURL& application_url,
+    WebAppUrlLoader& url_loader,
+    WebAppInstallFinalizer& install_finalizer,
+    base::OnceCallback<void(base::expected<InstallIsolatedAppCommandSuccess,
+                                           InstallIsolatedAppCommandError>)>
+        callback) {
+  if (!application_url.is_valid()) {
+    return base::unexpected{InstallIsolatedAppCommandCreateError{}};
+  }
+
+  return base::WrapUnique(new InstallIsolatedAppCommand(
+      application_url, url_loader, install_finalizer, std::move(callback)));
+}
+
 InstallIsolatedAppCommand::InstallIsolatedAppCommand(
     const GURL& url,
     WebAppUrlLoader& url_loader,
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.h b/chrome/browser/web_applications/commands/install_isolated_app_command.h
index 324350c0..c590760 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command.h
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command.h
@@ -44,6 +44,8 @@
   }
 };
 
+struct InstallIsolatedAppCommandCreateError {};
+
 class InstallIsolatedAppCommand : public WebAppCommand {
  public:
   // TODO(kuragin): Consider to create an instance of |GURL| instead of passing
@@ -55,13 +57,23 @@
   // |callback| must be not null.
   //
   // The `id` in the application's manifest must equal "/".
-  explicit InstallIsolatedAppCommand(
+  static base::expected<std::unique_ptr<InstallIsolatedAppCommand>,
+                        InstallIsolatedAppCommandCreateError>
+  Create(
       const GURL& application_url,
       WebAppUrlLoader& url_loader,
       WebAppInstallFinalizer& install_finalizer,
       base::OnceCallback<void(base::expected<InstallIsolatedAppCommandSuccess,
                                              InstallIsolatedAppCommandError>)>
           callback);
+
+  InstallIsolatedAppCommand(const InstallIsolatedAppCommand&) = delete;
+  InstallIsolatedAppCommand& operator=(const InstallIsolatedAppCommand&) =
+      delete;
+
+  InstallIsolatedAppCommand(InstallIsolatedAppCommand&&) = delete;
+  InstallIsolatedAppCommand& operator=(InstallIsolatedAppCommand&&) = delete;
+
   ~InstallIsolatedAppCommand() override;
 
   Lock& lock() const override;
@@ -76,6 +88,14 @@
       std::unique_ptr<WebAppDataRetriever> data_retriever);
 
  private:
+  explicit InstallIsolatedAppCommand(
+      const GURL& application_url,
+      WebAppUrlLoader& url_loader,
+      WebAppInstallFinalizer& install_finalizer,
+      base::OnceCallback<void(base::expected<InstallIsolatedAppCommandSuccess,
+                                             InstallIsolatedAppCommandError>)>
+          callback);
+
   void ReportFailure(base::StringPiece message);
   void ReportSuccess();
 
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
index 47e0f8b..2f333c1 100644
--- a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_future.h"
+#include "base/types/expected.h"
 #include "chrome/browser/web_applications/locks/lock.h"
 #include "chrome/browser/web_applications/test/fake_install_finalizer.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
@@ -156,8 +157,15 @@
       base::OnceCallback<void(base::expected<InstallIsolatedAppCommandSuccess,
                                              InstallIsolatedAppCommandError>)>
           callback) {
-    return std::make_unique<InstallIsolatedAppCommand>(
-        GURL(url), *url_loader_, *install_finalizer_, std::move(callback));
+    const GURL application_url{url};
+    DCHECK(application_url.is_valid());
+    base::expected<std::unique_ptr<InstallIsolatedAppCommand>,
+                   InstallIsolatedAppCommandCreateError>
+        command = InstallIsolatedAppCommand::Create(
+            application_url, *url_loader_, *install_finalizer_,
+            std::move(callback));
+    DCHECK(command.has_value());
+    return *std::move(command);
   }
 
   void ScheduleCommand(std::unique_ptr<WebAppCommand> command) {
@@ -355,14 +363,20 @@
               IsInstallationOk());
 }
 
-// It is impossible to pass invalid url to |GenerateAppId| since it DCHECKs.
-// TODO(kuragin): Replace constructor with factory function to make the
-// validation testable.
-TEST_F(InstallIsolatedAppCommandTest, DISABLED_ReportsErrorWhenURLIsInvalid) {
-  SetPrepareForLoadResultLoaded();
+TEST_F(InstallIsolatedAppCommandTest, ReportsErrorWhenURLIsInvalid) {
+  base::test::TestFuture<base::expected<InstallIsolatedAppCommandSuccess,
+                                        InstallIsolatedAppCommandError>>
+      test_future;
 
-  EXPECT_THAT(ExecuteCommand("some definetely invalid url"),
-              IsInstallationError(HasSubstr("Invalid application URL")));
+  auto url_loader = std::make_unique<TestWebAppUrlLoader>();
+
+  base::expected<std::unique_ptr<InstallIsolatedAppCommand>,
+                 InstallIsolatedAppCommandCreateError>
+      maybe_command = InstallIsolatedAppCommand::Create(
+          GURL{"some definetely invalid url"}, *url_loader, install_finalizer(),
+          test_future.GetCallback());
+
+  ASSERT_THAT(maybe_command.has_value(), IsFalse());
 }
 
 TEST_F(InstallIsolatedAppCommandTest, URLLoaderIgnoresQueryParameters) {
diff --git a/chrome/browser/web_applications/commands/web_app_install_command.cc b/chrome/browser/web_applications/commands/web_app_install_command.cc
deleted file mode 100644
index 99f8e70..0000000
--- a/chrome/browser/web_applications/commands/web_app_install_command.cc
+++ /dev/null
@@ -1,455 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/web_applications/commands/web_app_install_command.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/containers/flat_set.h"
-#include "base/feature_list.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/web_applications/install_bounce_metric.h"
-#include "chrome/browser/web_applications/locks/app_lock.h"
-#include "chrome/browser/web_applications/web_app_command_manager.h"
-#include "chrome/browser/web_applications/web_app_data_retriever.h"
-#include "chrome/browser/web_applications/web_app_id.h"
-#include "chrome/browser/web_applications/web_app_install_manager.h"
-#include "chrome/browser/web_applications/web_app_install_utils.h"
-#include "chrome/browser/web_applications/web_app_utils.h"
-#include "chrome/common/chrome_features.h"
-#include "components/webapps/browser/features.h"
-#include "components/webapps/browser/installable/installable_metrics.h"
-#include "content/public/browser/web_contents.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/components/arc/mojom/app.mojom.h"
-#include "ash/components/arc/mojom/intent_helper.mojom.h"
-#include "ash/components/arc/session/arc_bridge_service.h"
-#include "ash/components/arc/session/arc_service_manager.h"
-#include "base/strings/string_util.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS)
-#include "base/strings/utf_string_conversions.h"
-#include "net/base/url_util.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/crosapi/mojom/arc.mojom.h"
-#include "chromeos/crosapi/mojom/web_app_service.mojom.h"
-#include "chromeos/lacros/lacros_service.h"
-#include "chromeos/startup/browser_params_proxy.h"
-#endif
-
-namespace web_app {
-
-namespace {
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-constexpr bool kAddAppsToQuickLaunchBarByDefault = false;
-#else
-constexpr bool kAddAppsToQuickLaunchBarByDefault = true;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS)
-const char kChromeOsPlayPlatform[] = "chromeos_play";
-const char kPlayIntentPrefix[] =
-    "https://play.google.com/store/apps/details?id=";
-const char kPlayStorePackage[] = "com.android.vending";
-
-struct PlayStoreIntent {
-  std::string app_id;
-  std::string intent;
-};
-
-// Find the first Chrome OS app in related_applications of |manifest| and return
-// the details necessary to redirect the user to the app's listing in the Play
-// Store.
-absl::optional<PlayStoreIntent> GetPlayStoreIntentFromManifest(
-    const blink::mojom::Manifest& manifest) {
-  for (const auto& app : manifest.related_applications) {
-    std::string id = base::UTF16ToUTF8(app.id.value_or(std::u16string()));
-    if (!base::EqualsASCII(app.platform.value_or(std::u16string()),
-                           kChromeOsPlayPlatform)) {
-      continue;
-    }
-
-    if (id.empty()) {
-      // Fallback to ID in the URL.
-      if (!net::GetValueForKeyInQuery(app.url, "id", &id) || id.empty()) {
-        continue;
-      }
-    }
-
-    std::string referrer;
-    if (net::GetValueForKeyInQuery(app.url, "referrer", &referrer) &&
-        !referrer.empty()) {
-      referrer = "&referrer=" + referrer;
-    }
-
-    std::string intent = kPlayIntentPrefix + id + referrer;
-    return PlayStoreIntent{id, intent};
-  }
-  return absl::nullopt;
-}
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-bool ShouldInteractWithArc() {
-  auto* lacros_service = chromeos::LacrosService::Get();
-  return lacros_service &&
-         // Check if the feature is enabled.
-         chromeos::BrowserParamsProxy::Get()->WebAppsEnabled() &&
-         // Only use ARC installation flow if we know that remote ash-chrome is
-         // capable of installing from Play Store in lacros-chrome, to avoid
-         // redirecting users to the Play Store if they cannot install
-         // anything.
-         lacros_service->IsAvailable<crosapi::mojom::WebAppService>();
-}
-
-mojo::Remote<crosapi::mojom::Arc>* GetArcRemoteWithMinVersion(
-    uint32_t minVersion) {
-  auto* lacros_service = chromeos::LacrosService::Get();
-  if (lacros_service && lacros_service->IsAvailable<crosapi::mojom::Arc>() &&
-      lacros_service->GetInterfaceVersion(crosapi::mojom::Arc::Uuid_) >=
-          static_cast<int>(minVersion)) {
-    return &lacros_service->GetRemote<crosapi::mojom::Arc>();
-  }
-  return nullptr;
-}
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-
-}  // namespace
-
-WebAppInstallCommand::WebAppInstallCommand(
-    const AppId& app_id,
-    webapps::WebappInstallSource install_surface,
-    std::unique_ptr<WebAppInstallInfo> web_app_info,
-    blink::mojom::ManifestPtr opt_manifest,
-    const GURL& manifest_url,
-    WebAppInstallFlow flow,
-    WebAppInstallDialogCallback dialog_callback,
-    OnceInstallCallback callback,
-    Profile* profile,
-    WebAppInstallFinalizer* install_finalizer,
-    std::unique_ptr<WebAppDataRetriever> data_retriever,
-    base::WeakPtr<content::WebContents> contents)
-    : lock_(std::make_unique<AppLock, base::flat_set<AppId>>({app_id})),
-      app_id_(app_id),
-      install_surface_(install_surface),
-      web_app_info_(std::move(web_app_info)),
-      opt_manifest_(std::move(opt_manifest)),
-      manifest_url_(manifest_url),
-      flow_(flow),
-      dialog_callback_(std::move(dialog_callback)),
-      install_callback_(std::move(callback)),
-      profile_(profile),
-      install_finalizer_(install_finalizer),
-      data_retriever_(std::move(data_retriever)),
-      web_contents_(contents),
-      install_error_log_entry_(/*background_installation=*/false,
-                               install_surface_) {
-  DCHECK_NE(install_surface_, webapps::WebappInstallSource::SYNC);
-  DCHECK_NE(install_surface_, webapps::WebappInstallSource::SUB_APP);
-}
-
-WebAppInstallCommand::~WebAppInstallCommand() = default;
-
-Lock& WebAppInstallCommand::lock() const {
-  return *lock_;
-}
-
-void WebAppInstallCommand::Start() {
-  // This metric is recorded regardless of the installation result.
-  if (webapps::InstallableMetrics::IsReportableInstallSource(
-          install_surface_)) {
-    webapps::InstallableMetrics::TrackInstallEvent(install_surface_);
-  }
-
-  if (IsWebContentsDestroyed()) {
-    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
-    return;
-  }
-
-  DCHECK(AreWebAppsUserInstallable(profile_));
-
-  if (opt_manifest_)
-    UpdateWebAppInfoFromManifest(*opt_manifest_, manifest_url_,
-                                 web_app_info_.get());
-
-  if (flow_ == WebAppInstallFlow::kCreateShortcut &&
-      base::FeatureList::IsEnabled(
-          webapps::features::kCreateShortcutIgnoresManifest)) {
-    // When creating a shortcut, the |manifest_id| is not part of the App's
-    // primary key. The only thing that identifies a shortcut is the start URL,
-    // which is always set to the current page.
-    *web_app_info_ = WebAppInstallInfo::CreateInstallInfoForCreateShortcut(
-        web_contents_->GetLastCommittedURL(), *web_app_info_);
-  }
-
-  base::flat_set<GURL> icon_urls = GetValidIconUrlsToDownload(*web_app_info_);
-
-  // If the manifest specified icons, don't use the page icons.
-  const bool skip_page_favicons =
-      opt_manifest_ && !opt_manifest_->icons.empty();
-
-  CheckForPlayStoreIntentOrGetIcons(std::move(icon_urls), skip_page_favicons);
-}
-
-void WebAppInstallCommand::CheckForPlayStoreIntentOrGetIcons(
-    base::flat_set<GURL> icon_urls,
-    bool skip_page_favicons) {
-  bool is_create_shortcut = flow_ == WebAppInstallFlow::kCreateShortcut;
-  // Background installations are not a user-triggered installs, and thus
-  // cannot be sent to the store.
-  bool skip_store = is_create_shortcut || !opt_manifest_;
-
-  if (!skip_store) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    absl::optional<PlayStoreIntent> intent =
-        GetPlayStoreIntentFromManifest(*opt_manifest_);
-    if (intent) {
-      auto* arc_service_manager = arc::ArcServiceManager::Get();
-      if (arc_service_manager) {
-        auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
-            arc_service_manager->arc_bridge_service()->app(), IsInstallable);
-        if (instance) {
-          instance->IsInstallable(
-              intent->app_id,
-              base::BindOnce(
-                  &WebAppInstallCommand::OnDidCheckForIntentToPlayStore,
-                  weak_ptr_factory_.GetWeakPtr(), std::move(icon_urls),
-                  skip_page_favicons, intent->intent));
-          return;
-        }
-      }
-    }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    if (ShouldInteractWithArc()) {
-      absl::optional<PlayStoreIntent> intent =
-          GetPlayStoreIntentFromManifest(*opt_manifest_);
-      mojo::Remote<crosapi::mojom::Arc>* opt_arc = GetArcRemoteWithMinVersion(
-          crosapi::mojom::Arc::MethodMinVersions::kIsInstallableMinVersion);
-      if (opt_arc && intent) {
-        mojo::Remote<crosapi::mojom::Arc>& arc = *opt_arc;
-        arc->IsInstallable(
-            intent->app_id,
-            base::BindOnce(
-                &WebAppInstallCommand::OnDidCheckForIntentToPlayStoreLacros,
-                weak_ptr_factory_.GetWeakPtr(), std::move(icon_urls),
-                skip_page_favicons, intent->intent));
-        return;
-      }
-    }
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-  }
-  OnDidCheckForIntentToPlayStore(std::move(icon_urls), skip_page_favicons,
-                                 /*intent=*/"",
-                                 /*should_intent_to_store=*/false);
-}
-
-void WebAppInstallCommand::OnDidCheckForIntentToPlayStore(
-    base::flat_set<GURL> icon_urls,
-    bool skip_page_favicons,
-    const std::string& intent,
-    bool should_intent_to_store) {
-  if (IsWebContentsDestroyed()) {
-    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
-    return;
-  }
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (should_intent_to_store && !intent.empty()) {
-    auto* arc_service_manager = arc::ArcServiceManager::Get();
-    if (arc_service_manager) {
-      auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
-          arc_service_manager->arc_bridge_service()->intent_helper(),
-          HandleUrl);
-      if (instance) {
-        instance->HandleUrl(intent, kPlayStorePackage);
-        Abort(webapps::InstallResultCode::kIntentToPlayStore);
-        return;
-      }
-    }
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (ShouldInteractWithArc() && should_intent_to_store && !intent.empty()) {
-    mojo::Remote<crosapi::mojom::Arc>* opt_arc = GetArcRemoteWithMinVersion(
-        crosapi::mojom::Arc::MethodMinVersions::kHandleUrlMinVersion);
-    if (opt_arc) {
-      mojo::Remote<crosapi::mojom::Arc>& arc = *opt_arc;
-      arc->HandleUrl(intent, kPlayStorePackage);
-      Abort(webapps::InstallResultCode::kIntentToPlayStore);
-      return;
-    }
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-
-  data_retriever_->GetIcons(
-      web_contents_.get(), std::move(icon_urls), skip_page_favicons,
-      base::BindOnce(&WebAppInstallCommand::OnIconsRetrievedShowDialog,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-void WebAppInstallCommand::OnDidCheckForIntentToPlayStoreLacros(
-    base::flat_set<GURL> icon_urls,
-    bool skip_page_favicons,
-    const std::string& intent,
-    crosapi::mojom::IsInstallableResult result) {
-  OnDidCheckForIntentToPlayStore(
-      std::move(icon_urls), skip_page_favicons, intent,
-      result == crosapi::mojom::IsInstallableResult::kInstallable);
-}
-#endif
-
-void WebAppInstallCommand::OnIconsRetrievedShowDialog(
-    IconsDownloadedResult result,
-    IconsMap icons_map,
-    DownloadedIconsHttpResults icons_http_results) {
-  if (IsWebContentsDestroyed()) {
-    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
-    return;
-  }
-
-  DCHECK(web_app_info_);
-
-  PopulateProductIcons(web_app_info_.get(), &icons_map);
-  PopulateOtherIcons(web_app_info_.get(), icons_map);
-
-  RecordDownloadedIconsResultAndHttpStatusCodes(result, icons_http_results);
-  install_error_log_entry_.LogDownloadedIconsErrors(
-      *web_app_info_, result, icons_map, icons_http_results);
-
-  if (!dialog_callback_) {
-    OnDialogCompleted(/*user_accepted=*/true, std::move(web_app_info_));
-  } else {
-    std::move(dialog_callback_)
-        .Run(web_contents_.get(), std::move(web_app_info_),
-             base::BindOnce(&WebAppInstallCommand::OnDialogCompleted,
-                            weak_ptr_factory_.GetWeakPtr()));
-  }
-}
-
-void WebAppInstallCommand::OnDialogCompleted(
-    bool user_accepted,
-    std::unique_ptr<WebAppInstallInfo> web_app_info) {
-  if (IsWebContentsDestroyed()) {
-    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
-    return;
-  }
-
-  if (!user_accepted) {
-    Abort(webapps::InstallResultCode::kUserInstallDeclined);
-    return;
-  }
-
-  web_app_info_ = std::move(web_app_info);
-
-  WebAppInstallFinalizer::FinalizeOptions finalize_options(install_surface_);
-
-  finalize_options.locally_installed = true;
-  finalize_options.overwrite_existing_manifest_fields = true;
-  finalize_options.add_to_applications_menu = true;
-  finalize_options.add_to_desktop = true;
-  finalize_options.add_to_quick_launch_bar = kAddAppsToQuickLaunchBarByDefault;
-
-  install_finalizer_->FinalizeInstall(
-      *web_app_info_, finalize_options,
-      base::BindOnce(&WebAppInstallCommand::OnInstallFinalizedMaybeReparentTab,
-                     weak_ptr_factory_.GetWeakPtr()));
-
-  // Check that the finalizer hasn't called OnInstallFinalizedMaybeReparentTab
-  // synchronously:
-  DCHECK(install_callback_);
-}
-
-void WebAppInstallCommand::OnInstallFinalizedMaybeReparentTab(
-    const AppId& app_id,
-    webapps::InstallResultCode code,
-    OsHooksErrors os_hooks_errors) {
-  if (IsWebContentsDestroyed()) {
-    Abort(webapps::InstallResultCode::kWebContentsDestroyed);
-    return;
-  }
-
-  if (code != webapps::InstallResultCode::kSuccessNewInstall) {
-    Abort(code);
-    return;
-  }
-
-  RecordWebAppInstallationTimestamp(profile_->GetPrefs(), app_id,
-                                    install_surface_);
-
-  RecordAppBanner(web_contents_.get(), web_app_info_->start_url);
-
-  bool error = os_hooks_errors[OsHookType::kShortcuts];
-  const bool can_reparent_tab =
-      install_finalizer_->CanReparentTab(app_id, !error);
-
-  if (can_reparent_tab &&
-      (web_app_info_->user_display_mode != UserDisplayMode::kBrowser)) {
-    install_finalizer_->ReparentTab(app_id, !error, web_contents_.get());
-  }
-
-  OnInstallCompleted(app_id, webapps::InstallResultCode::kSuccessNewInstall);
-}
-
-bool WebAppInstallCommand::IsWebContentsDestroyed() {
-  return (!web_contents_ || web_contents_->IsBeingDestroyed());
-}
-
-void WebAppInstallCommand::Abort(webapps::InstallResultCode code) {
-  if (!install_callback_)
-    return;
-
-  webapps::InstallableMetrics::TrackInstallResult(false);
-  SignalCompletionAndSelfDestruct(
-      CommandResult::kFailure,
-      base::BindOnce(std::move(install_callback_), app_id_, code));
-}
-
-void WebAppInstallCommand::OnInstallCompleted(const AppId& app_id,
-                                              webapps::InstallResultCode code) {
-  if (base::FeatureList::IsEnabled(features::kRecordWebAppDebugInfo)) {
-    base::Value task_error_dict = install_error_log_entry_.TakeErrorDict();
-    if (!task_error_dict.DictEmpty())
-      command_manager()->LogToInstallManager(std::move(task_error_dict));
-  }
-
-  webapps::InstallableMetrics::TrackInstallResult(webapps::IsSuccess(code));
-  SignalCompletionAndSelfDestruct(
-      webapps::IsSuccess(code) ? CommandResult::kSuccess
-                               : CommandResult::kFailure,
-      base::BindOnce(std::move(install_callback_), app_id, code));
-}
-
-void WebAppInstallCommand::OnSyncSourceRemoved() {
-  // TODO(crbug.com/1320086): remove after uninstall from sync is async.
-  Abort(webapps::InstallResultCode::kAppNotInRegistrarAfterCommit);
-  return;
-}
-
-void WebAppInstallCommand::OnShutdown() {
-  Abort(webapps::InstallResultCode::kCancelledOnWebAppProviderShuttingDown);
-  return;
-}
-
-content::WebContents* WebAppInstallCommand::GetInstallingWebContents() {
-  return web_contents_.get();
-}
-
-base::Value WebAppInstallCommand::ToDebugValue() const {
-  return base::Value(base::StringPrintf("WebAppInstallCommand %d, app_id: %s",
-                                        id(), app_id_.c_str()));
-}
-}  // namespace web_app
diff --git a/chrome/browser/web_applications/commands/web_app_install_command.h b/chrome/browser/web_applications/commands/web_app_install_command.h
deleted file mode 100644
index afca5f51..0000000
--- a/chrome/browser/web_applications/commands/web_app_install_command.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_INSTALL_COMMAND_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_INSTALL_COMMAND_H_
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "base/values.h"
-#include "build/buildflag.h"
-#include "chrome/browser/web_applications/commands/web_app_command.h"
-#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
-#include "chrome/browser/web_applications/web_app_id.h"
-#include "chrome/browser/web_applications/web_app_install_params.h"
-#include "chrome/browser/web_applications/web_app_logging.h"
-#include "components/webapps/browser/installable/installable_metrics.h"
-#include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chromeos/crosapi/mojom/arc.mojom.h"
-#endif
-
-class Profile;
-
-namespace content {
-class WebContents;
-}
-
-namespace web_app {
-
-class AppLock;
-class WebAppDataRetriever;
-class WebAppInstallFinalizer;
-
-// Install the web app after the manifest is retrieved and validated.
-class WebAppInstallCommand : public WebAppCommand {
- public:
-  // When |dialog_callback| is null (aka |base::NullCallback|) the command
-  // doesn't show installation prompt in UI and installs the application in
-  // background.
-  WebAppInstallCommand(const AppId& app_id,
-                       webapps::WebappInstallSource install_surface,
-                       std::unique_ptr<WebAppInstallInfo> web_app_info,
-                       blink::mojom::ManifestPtr opt_manifest,
-                       const GURL& manifest_url,
-                       WebAppInstallFlow flow,
-                       WebAppInstallDialogCallback dialog_callback,
-                       OnceInstallCallback callback,
-                       Profile* profile,
-                       WebAppInstallFinalizer* install_finalizer,
-                       std::unique_ptr<WebAppDataRetriever> data_retriever,
-                       base::WeakPtr<content::WebContents> content);
-  ~WebAppInstallCommand() override;
-
-  Lock& lock() const override;
-
-  void Start() override;
-  void OnSyncSourceRemoved() override;
-  void OnShutdown() override;
-
-  content::WebContents* GetInstallingWebContents() override;
-
-  base::Value ToDebugValue() const override;
-
- private:
-  bool IsWebContentsDestroyed();
-
-  void Abort(webapps::InstallResultCode code);
-
-  void OnInstallCompleted(const AppId& app_id, webapps::InstallResultCode code);
-
-  // Either dispatches an asynchronous check for whether this installation
-  // should be stopped and an intent to the Play Store should be made, or
-  // synchronously calls OnDidCheckForIntentToPlayStore() implicitly failing the
-  // check if it cannot be made.
-  void CheckForPlayStoreIntentOrGetIcons(base::flat_set<GURL> icon_urls,
-                                         bool skip_page_favicons);
-
-  // Called when the asynchronous check for whether an intent to the Play Store
-  // should be made returns.
-  void OnDidCheckForIntentToPlayStore(base::flat_set<GURL> icon_urls,
-                                      bool skip_page_favicons,
-                                      const std::string& intent,
-                                      bool should_intent_to_store);
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // Called when the asynchronous check for whether an intent to the Play Store
-  // should be made returns (Lacros adapter that calls
-  // |OnDidCheckForIntentToPlayStore| based on |result|).
-  void OnDidCheckForIntentToPlayStoreLacros(
-      base::flat_set<GURL> icon_urls,
-      bool skip_page_favicons,
-      const std::string& intent,
-      crosapi::mojom::IsInstallableResult result);
-#endif
-
-  void OnIconsRetrievedShowDialog(
-      IconsDownloadedResult result,
-      IconsMap icons_map,
-      DownloadedIconsHttpResults icons_http_results);
-  void OnDialogCompleted(bool user_accepted,
-                         std::unique_ptr<WebAppInstallInfo> web_app_info);
-  void OnInstallFinalizedMaybeReparentTab(const AppId& app_id,
-                                          webapps::InstallResultCode code,
-                                          OsHooksErrors os_hooks_errors);
-
-  std::unique_ptr<AppLock> lock_;
-  AppId app_id_;
-  webapps::WebappInstallSource install_surface_;
-  std::unique_ptr<WebAppInstallInfo> web_app_info_;
-  blink::mojom::ManifestPtr opt_manifest_;
-  GURL manifest_url_;
-  WebAppInstallFlow flow_;
-  WebAppInstallDialogCallback dialog_callback_;
-  OnceInstallCallback install_callback_;
-
-  Profile* profile_;
-  WebAppInstallFinalizer* install_finalizer_;
-  std::unique_ptr<WebAppDataRetriever> data_retriever_;
-
-  base::WeakPtr<content::WebContents> web_contents_;
-
-  InstallErrorLogEntry install_error_log_entry_;
-
-  base::WeakPtrFactory<WebAppInstallCommand> weak_ptr_factory_{this};
-};
-
-}  // namespace web_app
-
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_INSTALL_COMMAND_H_
diff --git a/chrome/browser/web_applications/daily_metrics_helper.cc b/chrome/browser/web_applications/daily_metrics_helper.cc
index 7ef9d02f..0c09df8 100644
--- a/chrome/browser/web_applications/daily_metrics_helper.cc
+++ b/chrome/browser/web_applications/daily_metrics_helper.cc
@@ -172,8 +172,7 @@
 }
 
 void RemoveRecords(PrefService* prefs) {
-  DictionaryPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
-  update->DictClear();
+  prefs->SetDict(prefs::kWebAppsDailyMetrics, base::Value::Dict());
 }
 
 void UpdateRecord(DailyInteraction& record, PrefService* prefs) {
@@ -194,9 +193,9 @@
   }
 
   std::unique_ptr<DictionaryValue> record_dict = RecordToDict(record);
-  DictionaryPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
+  ScopedDictPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
 
-  update->SetKey(url, std::move(*record_dict));
+  update->Set(url, std::move(*record_dict));
 }
 
 }  // namespace
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
index c27bf74..d7af4e94 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
@@ -196,13 +196,13 @@
   dict.SetKey(kExtensionId, base::Value(app_id));
   dict.SetKey(kInstallSource, base::Value(static_cast<int>(install_source)));
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
-  update->SetKey(url.spec(), std::move(dict));
+  ScopedDictPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
+  update->Set(url.spec(), std::move(dict));
 }
 
 bool ExternallyInstalledWebAppPrefs::Remove(const GURL& url) {
-  DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
-  return update->RemoveKey(url.spec());
+  ScopedDictPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
+  return update->Remove(url.spec());
 }
 
 absl::optional<AppId> ExternallyInstalledWebAppPrefs::LookupAppId(
@@ -241,13 +241,13 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   DCHECK(pref_service_->GetDict(prefs::kWebAppsExtensionIDs).Find(url.spec()));
-  DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
-  base::Value* map = update.Get();
+  ScopedDictPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
+  base::Value::Dict& map = update.Get();
 
-  auto* app_entry = map->FindKey(url.spec());
+  auto* app_entry = map.FindDict(url.spec());
   DCHECK(app_entry);
 
-  app_entry->SetBoolKey(kIsPlaceholder, is_placeholder);
+  app_entry->Set(kIsPlaceholder, is_placeholder);
 }
 
 bool ExternallyInstalledWebAppPrefs::IsPlaceholderApp(
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs_browsertest.cc b/chrome/browser/web_applications/externally_installed_web_app_prefs_browsertest.cc
index 8390edf..c04f381 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs_browsertest.cc
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs_browsertest.cc
@@ -165,9 +165,9 @@
     ExternallyInstalledWebAppPrefsBrowserTest_ExternalPrefMigration,
     OldPrefFormat) {
   // Set up the old format for this pref {url -> app_id}.
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
+  ScopedDictPrefUpdate update(profile()->GetPrefs(),
                               prefs::kWebAppsExtensionIDs);
-  update->SetStringKey("https://example.com", "add_id_string");
+  update->Set("https://example.com", "add_id_string");
   // This should not crash on invalid pref data.
   EXPECT_FALSE(provider().registrar().IsPlaceholderApp(
       "app_id_string", WebAppManagement::kPolicy));
diff --git a/chrome/browser/web_applications/install_bounce_metric.cc b/chrome/browser/web_applications/install_bounce_metric.cc
index 3515616..02c015b 100644
--- a/chrome/browser/web_applications/install_bounce_metric.cc
+++ b/chrome/browser/web_applications/install_bounce_metric.cc
@@ -80,13 +80,12 @@
                                 PrefService* pref_service,
                                 const web_app::AppId& app_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey(kInstallTimestamp, SerializeTime(install_metrics.timestamp));
-  dict.SetKey(kInstallSource,
-              base::Value(static_cast<int>(install_metrics.source)));
+  base::Value::Dict dict;
+  dict.Set(kInstallTimestamp, SerializeTime(install_metrics.timestamp));
+  dict.Set(kInstallSource, static_cast<int>(install_metrics.source));
 
-  DictionaryPrefUpdate update(pref_service, prefs::kWebAppInstallMetrics);
-  update->SetKey(app_id, std::move(dict));
+  ScopedDictPrefUpdate update(pref_service, prefs::kWebAppInstallMetrics);
+  update->Set(app_id, std::move(dict));
 }
 
 }  // namespace
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc
index 50300c5..125d404 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc
@@ -69,11 +69,21 @@
                           },
                           std::move(url_loader));
 
-                  provider.command_manager().ScheduleCommand(
-                      std::make_unique<InstallIsolatedAppCommand>(
+                  auto [command_callback, immediate_callback] =
+                      base::SplitOnceCallback(std::move(callback));
+
+                  base::expected<std::unique_ptr<InstallIsolatedAppCommand>,
+                                 InstallIsolatedAppCommandCreateError>
+                      command = InstallIsolatedAppCommand::Create(
                           url, url_loader_ref, provider.install_finalizer(),
                           std::move(install_isolated_app_callback)
-                              .Then(std::move(callback))));
+                              .Then(std::move(command_callback)));
+                  if (command.has_value()) {
+                    provider.command_manager().ScheduleCommand(
+                        *std::move(command));
+                  } else {
+                    std::move(immediate_callback).Run();
+                  }
                 },
                 std::ref(provider), url, std::move(callback)));
       },
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils.cc b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
index 88af37d..106455ba 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
@@ -664,9 +664,9 @@
 void MarkAppAsMigratedToWebApp(Profile* profile,
                                const std::string& app_id,
                                bool was_migrated) {
-  ListPrefUpdate update(profile->GetPrefs(),
-                        webapps::kWebAppsMigratedPreinstalledApps);
-  base::Value::List& update_list = update->GetList();
+  ScopedListPrefUpdate update(profile->GetPrefs(),
+                              webapps::kWebAppsMigratedPreinstalledApps);
+  base::Value::List& update_list = update.Get();
   if (was_migrated)
     EnsureContains(update_list, app_id);
   else
@@ -688,9 +688,9 @@
 void SetMigrationRun(Profile* profile,
                      base::StringPiece feature_name,
                      bool was_migrated) {
-  ListPrefUpdate update(profile->GetPrefs(),
-                        prefs::kWebAppsDidMigrateDefaultChromeApps);
-  base::Value::List& update_list = update->GetList();
+  ScopedListPrefUpdate update(profile->GetPrefs(),
+                              prefs::kWebAppsDidMigrateDefaultChromeApps);
+  base::Value::List& update_list = update.Get();
   if (was_migrated)
     EnsureContains(update_list, feature_name);
   else
@@ -714,8 +714,8 @@
                                       const std::string& app_id) {
   if (WasPreinstalledAppUninstalled(profile, app_id))
     return;
-  ListPrefUpdate update(profile->GetPrefs(),
-                        prefs::kWebAppsUninstalledDefaultChromeApps);
-  EnsureContains(update->GetList(), app_id);
+  ScopedListPrefUpdate update(profile->GetPrefs(),
+                              prefs::kWebAppsUninstalledDefaultChromeApps);
+  EnsureContains(update.Get(), app_id);
 }
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/url_handler_prefs.cc b/chrome/browser/web_applications/url_handler_prefs.cc
index 657ebd6e..5bb1f92 100644
--- a/chrome/browser/web_applications/url_handler_prefs.cc
+++ b/chrome/browser/web_applications/url_handler_prefs.cc
@@ -368,14 +368,11 @@
 // Removes entries that match |profile_path| and |app_id|.
 // |profile_path| is always compared while |app_id| is only compared when it is
 // not empty.
-void RemoveEntries(base::Value& pref_value,
+void RemoveEntries(base::Value::Dict& pref_value,
                    const AppId& app_id,
                    const base::FilePath& profile_path) {
-  if (!pref_value.is_dict())
-    return;
-
   std::vector<std::string> origins_to_remove;
-  for (auto origin_value : pref_value.DictItems()) {
+  for (auto origin_value : pref_value) {
     base::Value::List handlers = std::move(origin_value.second.GetList());
     handlers.EraseIf([&app_id, &profile_path](const base::Value& handler) {
       return IsHandlerForApp(app_id, profile_path,
@@ -390,7 +387,7 @@
   }
 
   for (const auto& origin_to_remove : origins_to_remove)
-    pref_value.RemoveKey(origin_to_remove);
+    pref_value.Remove(origin_to_remove);
 }
 
 using PathSet = base::flat_set<std::string>;
@@ -543,10 +540,10 @@
                     const GURL& url,
                     const UrlHandlerSavedChoice choice,
                     const base::Time& time,
-                    base::Value& pref_value,
+                    base::Value::Dict& pref_value,
                     const std::string& origin_str,
                     const bool origin_trimmed) {
-  base::Value::List* handlers = pref_value.GetDict().FindList(origin_str);
+  base::Value::List* handlers = pref_value.FindList(origin_str);
   if (!handlers)
     return;
 
@@ -574,10 +571,8 @@
   DCHECK(choice != UrlHandlerSavedChoice::kInBrowser ||
          (app_id == nullptr && profile_path == nullptr));
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
 
   url::Origin origin = url::Origin::Create(url);
   if (origin.opaque())
@@ -590,9 +585,9 @@
 
   // SaveChoiceImpl modifies prefs but produces no output.
   TryDifferentOriginSubstrings(
-      origin_str, [app_id, profile_path, &url, choice, &time, pref_value](
+      origin_str, [app_id, profile_path, &url, choice, &time, &pref_value](
                       const std::string& origin_str, bool origin_trimmed) {
-        SaveChoiceImpl(app_id, profile_path, url, choice, time, *pref_value,
+        SaveChoiceImpl(app_id, profile_path, url, choice, time, pref_value,
                        origin_str, origin_trimmed);
       });
 }
@@ -706,10 +701,8 @@
   if (profile_path.empty() || url_handlers.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
 
   for (const apps::UrlHandlerInfo& handler_info : url_handlers) {
     const url::Origin& origin = handler_info.origin;
@@ -718,8 +711,7 @@
 
     base::Value new_handler(
         NewHandler(app_id, profile_path, handler_info, time));
-    base::Value::List* const handlers =
-        pref_value->GetDict().FindList(origin.Serialize());
+    base::Value::List* const handlers = pref_value.FindList(origin.Serialize());
     // One or more apps are already associated with this origin.
     if (handlers) {
       auto it = base::ranges::find_if(
@@ -737,7 +729,7 @@
     } else {
       base::Value::List new_handlers;
       new_handlers.Append(std::move(new_handler));
-      pref_value->GetDict().Set(origin.Serialize(), std::move(new_handlers));
+      pref_value.Set(origin.Serialize(), std::move(new_handlers));
     }
   }
 }
@@ -747,17 +739,15 @@
                   const base::FilePath& profile_path,
                   apps::UrlHandlers new_url_handlers,
                   const base::Time& time) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
 
   // In order to update data in URL handler prefs relevant to 'app_id' and
   // 'profile_path', perform an exhaustive search of all handler entries under
   // all keys. The previous url_handlers data could have had entries under any
   // origin key.
   std::vector<std::string> origins_to_remove;
-  for (auto origin_value : pref_value->DictItems()) {
+  for (auto origin_value : pref_value) {
     const std::string& origin_str = origin_value.first;
     base::Value::List curent_handlers =
         std::move(origin_value.second.GetList());
@@ -826,7 +816,7 @@
 
   // Remove any origin keys that have no more entries.
   for (const auto& origin_to_remove : origins_to_remove)
-    pref_value->RemoveKey(origin_to_remove);
+    pref_value.Remove(origin_to_remove);
 
   // Add the remaining items in 'new_url_handlers'.
   AddWebApp(local_state, app_id, profile_path, new_url_handlers, time);
@@ -838,12 +828,10 @@
   if (app_id.empty() || profile_path.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
 
-  RemoveEntries(*pref_value, app_id, profile_path);
+  RemoveEntries(pref_value, app_id, profile_path);
 }
 
 void RemoveProfile(PrefService* local_state,
@@ -851,12 +839,10 @@
   if (profile_path.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
 
-  RemoveEntries(*pref_value, /*app_id*/ "", profile_path);
+  RemoveEntries(pref_value, /*app_id*/ "", profile_path);
 }
 
 bool IsHandlerForProfile(const base::Value& handler,
@@ -885,9 +871,7 @@
 }
 
 void Clear(PrefService* local_state) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  pref_value->DictClear();
+  local_state->SetDict(prefs::kWebAppsUrlHandlerInfo, base::Value::Dict());
 }
 
 std::vector<UrlHandlerLaunchParams> FindMatchingUrlHandlers(
@@ -929,15 +913,13 @@
                       bool has_origin_wildcard,
                       const std::string& url_path,
                       const base::Time& time) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
-  base::Value* const pref_value = update.Get();
-  if (!pref_value || !pref_value->is_dict())
-    return;
-  base::Value* const handlers_mutable = pref_value->FindListKey(origin);
+  ScopedDictPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  base::Value::Dict& pref_value = update.Get();
+  base::Value::List* const handlers_mutable = pref_value.FindList(origin);
   if (!handlers_mutable)
     return;
 
-  for (auto& handler : handlers_mutable->GetListDeprecated()) {
+  for (auto& handler : *handlers_mutable) {
     auto handler_view = GetHandlerView(handler);
     if (!handler_view)
       continue;
diff --git a/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc b/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
index 0cf3b9a..a7283cb 100644
--- a/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
+++ b/chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.cc
@@ -48,9 +48,9 @@
         base::UserMetricsAction(kUserUninstalledPreinstalledAppAction));
   }
 
-  DictionaryPrefUpdate update(pref_service_,
+  ScopedDictPrefUpdate update(pref_service_,
                               prefs::kUserUninstalledPreinstalledWebAppPref);
-  update->SetKey(app_id, base::Value(std::move(url_list)));
+  update->Set(app_id, std::move(url_list));
 }
 
 absl::optional<AppId>
@@ -142,16 +142,16 @@
   if (install_urls.size() == url_list->size())
     return false;
 
-  DictionaryPrefUpdate update(pref_service_,
+  ScopedDictPrefUpdate update(pref_service_,
                               prefs::kUserUninstalledPreinstalledWebAppPref);
 
   // Add the URLs back to the pref and clear pref in case there are
   // app_ids with empty URLs.
   if (install_urls.size() == 0)
-    return update->RemoveKey(app_id);
+    return update->Remove(app_id);
 
   // Add the remaining URLs to the preinstalled prefs after deletion.
-  update->SetKey(app_id, base::Value(std::move(install_urls)));
+  update->Set(app_id, std::move(install_urls));
   return true;
 }
 
@@ -164,9 +164,9 @@
   // Pref does not contain the app_id, so no need of removal.
   if (!ids_to_urls.contains(app_id))
     return false;
-  DictionaryPrefUpdate update(pref_service_,
+  ScopedDictPrefUpdate update(pref_service_,
                               prefs::kUserUninstalledPreinstalledWebAppPref);
-  return update->RemoveKey(app_id);
+  return update->Remove(app_id);
 }
 
 bool UserUninstalledPreinstalledWebAppPrefs::AppIdContainsAllUrls(
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 3771bec..350e4aa 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1663761539-d8c347bff534468d1e5bdbac732c21017bc2e43f.profdata
+chrome-mac-main-1663783183-ba7af3e2dcda9927ad42413211122a2ce2a558c9.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 61398e7e..aa0f095c 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1663783183-2f667e488af4f8c4511571636864bb4c2addf119.profdata
+chrome-win64-main-1663793607-714c82dca7dd1c7176e7ad363a97731f5591fa80.profdata
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
index 3c524f5..23c63b3 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
@@ -177,13 +177,11 @@
   const base::Value* access_list_value =
       file_browser_handler->FindKey(keys::kFileAccessList);
   if (access_list_value) {
-    if (!access_list_value->is_list() ||
-        access_list_value->GetListDeprecated().empty()) {
+    if (!access_list_value->is_list() || access_list_value->GetList().empty()) {
       *error = errors::kInvalidFileAccessList;
       return nullptr;
     }
-    base::Value::ConstListView access_list_view =
-        access_list_value->GetListDeprecated();
+    const base::Value::List& access_list_view = access_list_value->GetList();
     for (size_t i = 0; i < access_list_view.size(); ++i) {
       const std::string* access = access_list_view[i].GetIfString();
       if (!access || result->AddFileAccessPermission(*access)) {
@@ -207,8 +205,7 @@
       *error = errors::kInvalidFileFiltersList;
       return nullptr;
     }
-    base::Value::ConstListView file_filters_list =
-        file_filters->GetListDeprecated();
+    const base::Value::List& file_filters_list = file_filters->GetList();
     for (size_t i = 0; i < file_filters_list.size(); ++i) {
       const std::string* filter_in = file_filters_list[i].GetIfString();
       if (!filter_in) {
@@ -261,7 +258,7 @@
 
 // Loads FileBrowserHandlers from |extension_actions| into a list in |result|.
 bool LoadFileBrowserHandlers(const std::string& extension_id,
-                             const base::Value::ConstListView extension_actions,
+                             const base::Value::List& extension_actions,
                              FileBrowserHandler::List* result,
                              std::u16string* error) {
   for (const auto& entry : extension_actions) {
@@ -303,7 +300,7 @@
 
   std::unique_ptr<FileBrowserHandlerInfo> info(new FileBrowserHandlerInfo);
   if (!LoadFileBrowserHandlers(extension->id(),
-                               file_browser_handlers_value->GetListDeprecated(),
+                               file_browser_handlers_value->GetList(),
                                &info->file_browser_handlers, error)) {
     return false;  // Failed to parse file browser actions definition.
   }
diff --git a/chrome/common/extensions/api/input_ime/input_components_handler.cc b/chrome/common/extensions/api/input_ime/input_components_handler.cc
index d27cf17..6c32a56 100644
--- a/chrome/common/extensions/api/input_ime/input_components_handler.cc
+++ b/chrome/common/extensions/api/input_ime/input_components_handler.cc
@@ -55,8 +55,8 @@
   }
 
   auto info = std::make_unique<InputComponents>();
-  for (size_t i = 0; i < list_value->GetListDeprecated().size(); ++i) {
-    const base::Value& module_value = list_value->GetListDeprecated()[i];
+  for (size_t i = 0; i < list_value->GetList().size(); ++i) {
+    const base::Value& module_value = list_value->GetList()[i];
     if (!module_value.is_dict()) {
       *error = errors::kInvalidInputComponents16;
       return false;
@@ -85,7 +85,7 @@
       if (language_value->is_string()) {
         languages.insert(language_value->GetString());
       } else if (language_value->is_list()) {
-        for (const auto& language : language_value->GetListDeprecated()) {
+        for (const auto& language : language_value->GetList()) {
           if (language.is_string())
             languages.insert(language.GetString());
         }
@@ -96,8 +96,8 @@
     std::set<std::string> layouts;
     const base::Value* layouts_value = module_value.FindListKey(keys::kLayouts);
     if (layouts_value) {
-      for (size_t j = 0; j < layouts_value->GetListDeprecated().size(); ++j) {
-        const auto& layout = layouts_value->GetListDeprecated()[j];
+      for (size_t j = 0; j < layouts_value->GetList().size(); ++j) {
+        const auto& layout = layouts_value->GetList()[j];
         if (!layout.is_string()) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
               errors::kInvalidInputComponentLayoutName, base::NumberToString(i),
diff --git a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
index 1b043526..39b648b 100644
--- a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
+++ b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
@@ -34,7 +34,7 @@
 TtsVoices::~TtsVoices() = default;
 
 //  static
-bool TtsVoices::Parse(base::Value::ConstListView tts_voices,
+bool TtsVoices::Parse(const base::Value::List& tts_voices,
                       TtsVoices* out_voices,
                       std::u16string* error,
                       Extension* extension) {
@@ -90,8 +90,7 @@
         *error = errors::kInvalidTtsVoicesEventTypes;
         return false;
       }
-      for (const base::Value& event_type_val :
-           event_types->GetListDeprecated()) {
+      for (const base::Value& event_type_val : event_types->GetList()) {
         if (!event_type_val.is_string()) {
           *error = errors::kInvalidTtsVoicesEventTypes;
           return false;
@@ -155,8 +154,7 @@
     return false;
   }
 
-  if (!TtsVoices::Parse(tts_voices->GetListDeprecated(), info.get(), error,
-                        extension))
+  if (!TtsVoices::Parse(tts_voices->GetList(), info.get(), error, extension))
     return false;
 
   const base::Value* tts_engine_sample_rate =
diff --git a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
index 4288b41..c9b5f0a5 100644
--- a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
+++ b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
@@ -32,7 +32,7 @@
 struct TtsVoices : public Extension::ManifestData {
   TtsVoices();
   ~TtsVoices() override;
-  static bool Parse(base::Value::ConstListView tts_voices,
+  static bool Parse(const base::Value::List& tts_voices,
                     TtsVoices* out_voices,
                     std::u16string* error,
                     Extension* extension);
diff --git a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
index b20c3dfd..28a3143 100644
--- a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
+++ b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
@@ -127,13 +127,13 @@
 
   const base::ListValue* manif_patterns = nullptr;
   if (!handler_info.GetList(mkeys::kMatches, &manif_patterns) ||
-      manif_patterns->GetListDeprecated().size() == 0) {
+      manif_patterns->GetList().size() == 0) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         merrors::kInvalidURLHandlerPattern, handler_id);
     return false;
   }
 
-  for (const auto& entry : manif_patterns->GetListDeprecated()) {
+  for (const auto& entry : manif_patterns->GetList()) {
     std::string str_pattern =
         entry.is_string() ? entry.GetString() : std::string();
     // TODO(sergeygs): Limit this to non-top-level domains.
diff --git a/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc
index f5e0796..6625f3b8 100644
--- a/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc
+++ b/chrome/common/extensions/manifest_handlers/natively_connectable_handler.cc
@@ -50,7 +50,7 @@
   }
 
   auto hosts = std::make_unique<NativelyConnectableHosts>();
-  for (const auto& host : natively_connectable_hosts->GetListDeprecated()) {
+  for (const auto& host : natively_connectable_hosts->GetList()) {
     if (!host.is_string() || host.GetString().empty()) {
       *error = manifest_errors::kInvalidNativelyConnectableValue16;
       return false;
diff --git a/chrome/common/extensions/manifest_handlers/theme_handler.cc b/chrome/common/extensions/manifest_handlers/theme_handler.cc
index 254da4e3..aadd286 100644
--- a/chrome/common/extensions/manifest_handlers/theme_handler.cc
+++ b/chrome/common/extensions/manifest_handlers/theme_handler.cc
@@ -63,7 +63,7 @@
         *error = errors::kInvalidThemeColors;
         return false;
       }
-      base::Value::ConstListView color_list = it.second.GetListDeprecated();
+      const base::Value::List& color_list = it.second.GetList();
 
       // There must be either 3 items (RGB), or 4 (RGBA).
       if (!(color_list.size() == 3 || color_list.size() == 4)) {
@@ -106,7 +106,7 @@
       return false;
     }
 
-    base::Value::ConstListView tint_list = item.second.GetListDeprecated();
+    const base::Value::List& tint_list = item.second.GetList();
     if (tint_list.size() != 3) {
       *error = errors::kInvalidThemeTints;
       return false;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3044f42..7e902164 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3418,7 +3418,7 @@
         "../browser/ui/views/status_bubble_views_browsertest.cc",
         "../browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc",
         "../browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc",
-        "../browser/ui/views/tabs/tab_hover_card_bubble_view_browsertest.cc",
+        "../browser/ui/views/tabs/tab_hover_card_bubble_view_dialog_browsertest.cc",
         "../browser/ui/views/toolbar/browser_app_menu_browsertest.cc",
         "../browser/ui/views/translate/translate_bubble_test_utils_views.cc",
         "../browser/ui/views/translate/translate_bubble_view_browsertest.cc",
@@ -4187,6 +4187,7 @@
         "//chrome/browser/ash:add_remove_user_event_proto",
         "//chrome/browser/ash:arc_test_support",
         "//chrome/browser/ash:key_permissions_proto",
+        "//chrome/browser/ash:login_logout_event_proto",
         "//chrome/browser/ash/crosapi",
         "//chrome/browser/ash/crosapi:test_support",
         "//chrome/browser/ash/login/oobe_quick_start/connectivity:test_support",
@@ -4195,7 +4196,6 @@
         "//chrome/browser/ash/wilco_dtc_supportd:mojo_utils",
         "//chrome/browser/chromeos",
         "//chrome/browser/chromeos:dlp_policy_event_proto",
-        "//chrome/browser/chromeos:login_logout_event_proto",
         "//chrome/browser/chromeos:test_support",
         "//chrome/browser/enterprise/connectors/device_trust:features",
         "//chrome/browser/enterprise/connectors/device_trust/common",
@@ -9021,6 +9021,8 @@
       "../browser/ui/search/ntp_test_utils.h",
       "../browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.cc",
       "../browser/ui/views/autofill/payments/offer_notification_bubble_views_test_base.h",
+      "../browser/ui/views/tabs/tab_hover_card_test_util.cc",
+      "../browser/ui/views/tabs/tab_hover_card_test_util.h",
       "../browser/ui/views/web_apps/web_app_integration_test_driver.cc",
       "../browser/ui/views/web_apps/web_app_integration_test_driver.h",
       "../browser/ui/web_applications/test/web_app_navigation_browsertest.cc",
@@ -9149,6 +9151,7 @@
       "//ui/base:features",
       "//ui/base:test_support",
       "//ui/events:test_support",
+      "//ui/gfx:test_support",
       "//ui/shell_dialogs",
       "//ui/views",
       "//ui/views:test_support",
@@ -9659,7 +9662,7 @@
         "../browser/ui/views/ssl_client_certificate_selector_browsertest.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc",
         "../browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h",
-        "../browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc",
+        "../browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc",
         "../browser/ui/views/test/view_event_test_base.cc",
         "../browser/ui/views/test/view_event_test_base.h",
         "../browser/ui/views/toolbar/reload_button_browsertest.cc",
@@ -9687,6 +9690,7 @@
         "//components/optimization_guide/core:test_support",
         "//components/optimization_guide/proto:optimization_guide_proto",
         "//components/performance_manager:performance_manager",
+        "//components/reputation/core:core",
         "//components/user_education/views",
         "//ui/base/dragdrop:types",
         "//ui/base/dragdrop/mojom",
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/all_inputs_disabled_test.js b/chrome/test/data/webui/chromeos/shimless_rma/all_inputs_disabled_test.js
index 81ddd5b..2f5f7c5 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/all_inputs_disabled_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/all_inputs_disabled_test.js
@@ -51,7 +51,7 @@
   // load.
   function setupFakeService() {
     // kUpdateOs
-    service.setGetCurrentOsVersionResult('');
+    service.setGetCurrentOsVersionResult(null);
 
     // kEnterRSUWPDisableCode
     service.setGetRsuDisableWriteProtectChallengeResult('');
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index d9be552..a0b25f1 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -104,8 +104,9 @@
   });
 
   test('GetCurrentOsVersionDefaultUndefined', () => {
+    service.setGetCurrentOsVersionResult(null);
     return service.getCurrentOsVersion().then((version) => {
-      assertEquals(version, undefined);
+      assertEquals(version.version, null);
     });
   });
 
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/reimaging_firmware_update_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/reimaging_firmware_update_page_test.js
index dd5301e0..a930ca00 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/reimaging_firmware_update_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/reimaging_firmware_update_page_test.js
@@ -7,11 +7,20 @@
 import {FakeShimlessRmaService} from 'chrome://shimless-rma/fake_shimless_rma_service.js';
 import {setShimlessRmaServiceForTesting} from 'chrome://shimless-rma/mojo_interface_provider.js';
 import {UpdateRoFirmwarePage} from 'chrome://shimless-rma/reimaging_firmware_update_page.js';
+import {ShimlessRma} from 'chrome://shimless-rma/shimless_rma.js';
 import {UpdateRoFirmwareStatus} from 'chrome://shimless-rma/shimless_rma_types.js';
+
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks} from '../../test_util.js';
 
 export function reimagingFirmwareUpdatePageTest() {
+  /**
+   * ShimlessRma is needed to handle the 'transition-state' event used
+   * when handling calibration overall progress signals.
+   * @type {?ShimlessRma}
+   */
+  let shimless_rma_component = null;
+
   /** @type {?UpdateRoFirmwarePage} */
   let component = null;
 
@@ -25,6 +34,8 @@
   });
 
   teardown(() => {
+    shimless_rma_component.remove();
+    shimless_rma_component = null;
     component.remove();
     component = null;
     service.reset();
@@ -34,6 +45,11 @@
   function initializeReimagingFirmwareUpdatePage() {
     assertFalse(!!component);
 
+    shimless_rma_component =
+        /** @type {!ShimlessRma} */ (document.createElement('shimless-rma'));
+    assertTrue(!!shimless_rma_component);
+    document.body.appendChild(shimless_rma_component);
+
     component = /** @type {!UpdateRoFirmwarePage} */ (
         document.createElement('reimaging-firmware-update-page'));
     assertTrue(!!component);
@@ -51,67 +67,65 @@
     assertEquals('', updateStatus.textContent.trim());
   });
 
-  test('RoFirmwareUpdateStartingDisablesNext', async () => {
-    await initializeReimagingFirmwareUpdatePage();
+  test(
+      'UnpluggingUsbDoesntTriggerTransitionToNextPageIfUpdateIsNotComplete',
+      async () => {
+        const resolver = new PromiseResolver();
 
-    let savedResult;
-    let savedError;
-    component.onNextButtonClick()
-        .then((result) => savedResult = result)
-        .catch((error) => savedError = error);
-    await flushTasks();
+        let callCount = 0;
+        service.roFirmwareUpdateComplete = () => {
+          callCount++;
+          return resolver.promise;
+        };
 
-    assertTrue(savedError instanceof Error);
-    assertEquals(savedError.message, 'RO Firmware update is not complete.');
-    assertEquals(savedResult, undefined);
-  });
+        await initializeReimagingFirmwareUpdatePage();
+        service.triggerExternalDiskObserver(false, 0);
+        await flushTasks();
+        assertEquals(0, callCount);
 
-  test('RoFirmwareUpdateInProgressDisablesNext', async () => {
-    await initializeReimagingFirmwareUpdatePage();
-    service.triggerUpdateRoFirmwareObserver(
-        UpdateRoFirmwareStatus.kUpdating, 0);
-    await flushTasks();
+        service.triggerUpdateRoFirmwareObserver(
+            UpdateRoFirmwareStatus.kUpdating, 0);
+        await flushTasks();
 
-    let savedResult;
-    let savedError;
-    component.onNextButtonClick()
-        .then((result) => savedResult = result)
-        .catch((error) => savedError = error);
-    await flushTasks();
+        service.triggerExternalDiskObserver(false, 0);
+        await flushTasks();
+        assertEquals(0, callCount);
+      });
 
-    assertTrue(savedError instanceof Error);
-    assertEquals(savedError.message, 'RO Firmware update is not complete.');
-    assertEquals(savedResult, undefined);
-  });
+  test(
+      'UnpluggingUsbTriggersTransitionToNextPageIfUpdateIsComplete',
+      async () => {
+        const resolver = new PromiseResolver();
+        await initializeReimagingFirmwareUpdatePage();
 
-  test('RoFirmwareUpdateEnablesNext', async () => {
-    const resolver = new PromiseResolver();
-    await initializeReimagingFirmwareUpdatePage();
+        const firmwareTitle = component.shadowRoot.querySelector('#titleText');
+        assertEquals(
+            loadTimeData.getString('firmwareUpdateInstallImageTitleText'),
+            firmwareTitle.textContent.trim());
 
-    const firmwareTitle = component.shadowRoot.querySelector('#titleText');
-    assertEquals(
-        loadTimeData.getString('firmwareUpdateInstallImageTitleText'),
-        firmwareTitle.textContent.trim());
+        let callCount = 0;
+        service.roFirmwareUpdateComplete = () => {
+          callCount++;
+          return resolver.promise;
+        };
 
-    service.triggerUpdateRoFirmwareObserver(
-        UpdateRoFirmwareStatus.kComplete, 0);
-    await flushTasks();
-    service.roFirmwareUpdateComplete = () => {
-      return resolver.promise;
-    };
+        // Complete the update.
+        service.triggerUpdateRoFirmwareObserver(
+            UpdateRoFirmwareStatus.kComplete, 0);
+        await flushTasks();
 
-    // Confirm the page title changes after firmware install completes.
-    assertEquals(
-        loadTimeData.getString('firmwareUpdateInstallCompleteTitleText'),
-        firmwareTitle.textContent.trim());
+        // Make sure that the transition doesn't happen until the USB is
+        // unplugged.
+        assertEquals(0, callCount);
 
-    const expectedResult = {foo: 'bar'};
-    let savedResult;
-    component.onNextButtonClick().then((result) => savedResult = result);
-    // Resolve to a distinct result to confirm it was not modified.
-    resolver.resolve(expectedResult);
-    await flushTasks();
+        // Confirm the page title changes after firmware install completes.
+        assertEquals(
+            loadTimeData.getString('firmwareUpdateInstallCompleteTitleText'),
+            firmwareTitle.textContent.trim());
 
-    assertDeepEquals(savedResult, expectedResult);
-  });
+        // Unplug the USB and verify that the transition happened.
+        service.triggerExternalDiskObserver(false, 0);
+        await flushTasks();
+        assertEquals(1, callCount);
+      });
 }
diff --git a/chrome/test/data/webui/settings/password_view_test.ts b/chrome/test/data/webui/settings/password_view_test.ts
index 0d4ee14..a6ddba8 100644
--- a/chrome/test/data/webui/settings/password_view_test.ts
+++ b/chrome/test/data/webui/settings/password_view_test.ts
@@ -120,10 +120,10 @@
                   assertEquals(
                       NOTE,
                       page.shadowRoot!.querySelector(
-                                          'settings-textarea')!.value);
+                                          '#note')!.innerHTML.trim());
                 } else {
-                  assertFalse(isVisible(
-                      page.shadowRoot!.querySelector('settings-textarea')));
+                  assertFalse(
+                      isVisible(page.shadowRoot!.querySelector('#note')));
                 }
               }));
 
@@ -203,7 +203,7 @@
     const page = await loadViewPage(passwordEntry);
     assertEquals(
         'No note added',
-        page.shadowRoot!.querySelector('settings-textarea')!.value);
+        page.shadowRoot!.querySelector('#note')!.innerHTML.trim());
   });
 
   test('Federated credential layout', async function() {
diff --git a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
index 63aa289..a1c9b7a 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_test.ts
@@ -272,19 +272,27 @@
         window.localStorage[LOCAL_STORAGE_OPEN_FOLDERS_KEY]);
   });
 
-  test('ShoppingListVisibility', () => {
+  test('ShoppingListVisibility', async () => {
     checkShoppingListVisibility(bookmarksList, true);
     assertEquals(
         1,
         metrics.count('Commerce.PriceTracking.SidePanel.TrackedProductsShown'));
 
     shoppingListApi.setProducts([]);
-    const bookmarksListNoShopping = document.createElement('bookmarks-list');
-    document.body.appendChild(bookmarksListNoShopping);
+    const newbookmarksList = document.createElement('bookmarks-list');
+    document.body.appendChild(newbookmarksList);
 
-    checkShoppingListVisibility(bookmarksListNoShopping, false);
+    checkShoppingListVisibility(newbookmarksList, false);
     assertEquals(
         1,
         metrics.count('Commerce.PriceTracking.SidePanel.TrackedProductsShown'));
+
+    shoppingListApi.getCallbackRouterRemote().priceTrackedForBookmark(
+        products[0]!);
+    await flushTasks();
+    checkShoppingListVisibility(newbookmarksList, true);
+    assertEquals(
+        2,
+        metrics.count('Commerce.PriceTracking.SidePanel.TrackedProductsShown'));
   });
 });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
index 54ea171..8b8cb1d 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/commerce/shopping_list_test.ts
@@ -125,7 +125,7 @@
     ShoppingListApiProxyImpl.setInstance(shoppingListApi);
 
     shoppingList = document.createElement('shopping-list');
-    shoppingList.productInfos = products;
+    shoppingList.productInfos = products.slice();
     document.body.appendChild(shoppingList);
 
     await flushTasks();
@@ -337,4 +337,34 @@
     await flushTasks();
     checkActionButtonStatus(actionButtonA, true);
   });
+
+  test('ObservesTrackedProductInfoUpdate', async () => {
+    let productElements = getProductElements(shoppingList);
+    assertEquals(2, products.length);
+
+    for (let i = 0; i < products.length; i++) {
+      checkProductElementRender(productElements[i]!, products[i]!);
+    }
+
+    const updatedProduct = {
+      bookmarkId: BigInt(3),
+      info: {
+        title: 'Product Baz',
+        domain: 'baz.com',
+        imageUrl: {url: 'https://baz.com/image'},
+        productUrl: {url: 'https://baz.com/product'},
+        currentPrice: '$56',
+        previousPrice: '$78',
+      },
+    };
+    shoppingListApi.getCallbackRouterRemote().priceTrackedForBookmark(
+        updatedProduct);
+    await flushTasks();
+
+    productElements = getProductElements(shoppingList);
+    assertEquals(2, products.length);
+
+    checkProductElementRender(productElements[0]!, updatedProduct);
+    checkProductElementRender(productElements[1]!, products[1]!);
+  });
 });
diff --git a/chrome/updater/app/server/win/server.cc b/chrome/updater/app/server/win/server.cc
index 7ccfbdf..5150697f 100644
--- a/chrome/updater/app/server/win/server.cc
+++ b/chrome/updater/app/server/win/server.cc
@@ -23,9 +23,11 @@
 #include "base/strings/strcat.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/win/atl.h"
 #include "base/win/registry.h"
+#include "base/win/scoped_handle.h"
 #include "base/win/windows_types.h"
 #include "chrome/installer/util/work_item_list.h"
 #include "chrome/updater/app/server/win/com_classes.h"
@@ -136,6 +138,22 @@
   return true;
 }
 
+// Signal the legacy GoogleUpdate processes to exit.
+void SignalShutdownEvent(UpdaterScope scope) {
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(kShutdownEvent, scope, &attr);
+
+  base::WaitableEvent event(base::win::ScopedHandle(
+      ::CreateEvent(&attr.sa, true, false, attr.name.c_str())));
+  if (!event.handle()) {
+    VLOG(1) << __func__ << "Could not create the shutdown event: " << std::hex
+            << HRESULTFromLastError();
+    return;
+  }
+
+  event.Signal();
+}
+
 // Install Updater.exe as GoogleUpdate.exe in the file system under
 // Google\Update. And add a "pv" registry value under the
 // UPDATER_KEY\Clients\{GoogleUpdateAppId}.
@@ -147,9 +165,10 @@
                       WorkItemList* list) {
   DCHECK(list);
 
-  // TODO(crbug.com/1290496) Do we need to set the shutdown event and wait or
-  // kill any running GoogleUpdate.exe instances? If so, is waiting a good idea
-  // during the swap?
+  // TODO(crbug.com/1290496) Do we need to force kill and wait for any running
+  // GoogleUpdate.exe instances?
+  SignalShutdownEvent(scope);
+
   const absl::optional<base::FilePath> target_path =
       GetGoogleUpdateExePath(scope);
   if (!target_path)
diff --git a/chrome/updater/win/win_constants.h b/chrome/updater/win/win_constants.h
index a3a0b08..a83e45b 100644
--- a/chrome/updater/win/win_constants.h
+++ b/chrome/updater/win/win_constants.h
@@ -79,6 +79,9 @@
 extern const wchar_t kWindowsServiceName[];
 extern const wchar_t kWindowsInternalServiceName[];
 
+// Windows event name used to signal the legacy GoogleUpdate processes to exit.
+constexpr wchar_t kShutdownEvent[] = L"{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}";
+
 // crbug.com/1259178: there is a race condition on activating the COM service
 // and the service shutdown. The race condition is likely to occur when a new
 // instance of an updater coclass is created right after the last reference
diff --git a/chromeos/components/mojo_service_manager/BUILD.gn b/chromeos/ash/components/mojo_service_manager/BUILD.gn
similarity index 73%
rename from chromeos/components/mojo_service_manager/BUILD.gn
rename to chromeos/ash/components/mojo_service_manager/BUILD.gn
index 1fdf549..4f1b367 100644
--- a/chromeos/components/mojo_service_manager/BUILD.gn
+++ b/chromeos/ash/components/mojo_service_manager/BUILD.gn
@@ -2,9 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//chromeos/features.gni")
 
-assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //chromeos/ash")
 
 component("mojo_service_manager") {
   sources = [
@@ -16,7 +17,7 @@
     "//chromeos:features",
     "//mojo/public/cpp/bindings",
   ]
-  public_deps = [ "//chromeos/components/mojo_service_manager/mojom" ]
+  public_deps = [ "//chromeos/ash/components/mojo_service_manager/mojom" ]
   defines = [ "IS_CHROMEOS_MOJO_SERVICE_MANAGER_IMPL" ]
 
   if (!use_real_chromeos_services) {
diff --git a/chromeos/components/mojo_service_manager/DIR_METADATA b/chromeos/ash/components/mojo_service_manager/DIR_METADATA
similarity index 100%
rename from chromeos/components/mojo_service_manager/DIR_METADATA
rename to chromeos/ash/components/mojo_service_manager/DIR_METADATA
diff --git a/chromeos/components/mojo_service_manager/OWNERS b/chromeos/ash/components/mojo_service_manager/OWNERS
similarity index 100%
rename from chromeos/components/mojo_service_manager/OWNERS
rename to chromeos/ash/components/mojo_service_manager/OWNERS
diff --git a/chromeos/components/mojo_service_manager/README.md b/chromeos/ash/components/mojo_service_manager/README.md
similarity index 81%
rename from chromeos/components/mojo_service_manager/README.md
rename to chromeos/ash/components/mojo_service_manager/README.md
index 01963e8a4c..d59544b 100644
--- a/chromeos/components/mojo_service_manager/README.md
+++ b/chromeos/ash/components/mojo_service_manager/README.md
@@ -5,5 +5,5 @@
 the service manager.
 
 The API can be found at
-[mojom/mojo_service_manager.mojom](https://source.chromium.org/chromium/chromium/src/+/HEAD:chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom)
+[mojom/mojo_service_manager.mojom](https://source.chromium.org/chromium/chromium/src/+/HEAD:chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom)
 and the full document can be found at [ChromeOS://src/platform2/mojo_service_manager/](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/HEAD:src/platform2/mojo_service_manager/)
diff --git a/chromeos/components/mojo_service_manager/connection.cc b/chromeos/ash/components/mojo_service_manager/connection.cc
similarity index 98%
rename from chromeos/components/mojo_service_manager/connection.cc
rename to chromeos/ash/components/mojo_service_manager/connection.cc
index 2c9de3f..9e88615 100644
--- a/chromeos/components/mojo_service_manager/connection.cc
+++ b/chromeos/ash/components/mojo_service_manager/connection.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 
 #include <errno.h>
 #include <sys/socket.h>
diff --git a/chromeos/components/mojo_service_manager/connection.h b/chromeos/ash/components/mojo_service_manager/connection.h
similarity index 82%
rename from chromeos/components/mojo_service_manager/connection.h
rename to chromeos/ash/components/mojo_service_manager/connection.h
index 6d3466b..607be03a 100644
--- a/chromeos/components/mojo_service_manager/connection.h
+++ b/chromeos/ash/components/mojo_service_manager/connection.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
-#define CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
+#define CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
 
 #include "base/component_export.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 
 namespace chromeos::mojo_service_manager {
@@ -38,4 +38,4 @@
 
 }  // namespace chromeos::mojo_service_manager
 
-#endif  // CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_CONNECTION_H_
diff --git a/chromeos/components/mojo_service_manager/fake_mojo_service_manager.cc b/chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.cc
similarity index 98%
rename from chromeos/components/mojo_service_manager/fake_mojo_service_manager.cc
rename to chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.cc
index 200b560..a2e1633 100644
--- a/chromeos/components/mojo_service_manager/fake_mojo_service_manager.cc
+++ b/chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/mojo_service_manager/fake_mojo_service_manager.h"
+#include "chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h"
 
 #include <utility>
 
diff --git a/chromeos/components/mojo_service_manager/fake_mojo_service_manager.h b/chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h
similarity index 90%
rename from chromeos/components/mojo_service_manager/fake_mojo_service_manager.h
rename to chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h
index dd9d1a43..b5164af 100644
--- a/chromeos/components/mojo_service_manager/fake_mojo_service_manager.h
+++ b/chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
-#define CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
+#ifndef CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
+#define CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
 
 #include <string>
 #include <vector>
 
 #include "base/component_export.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -85,4 +85,4 @@
 
 }  // namespace chromeos::mojo_service_manager
 
-#endif  // CHROMEOS_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
+#endif  // CHROMEOS_ASH_COMPONENTS_MOJO_SERVICE_MANAGER_FAKE_MOJO_SERVICE_MANAGER_H_
diff --git a/chromeos/components/mojo_service_manager/mojom/BUILD.gn b/chromeos/ash/components/mojo_service_manager/mojom/BUILD.gn
similarity index 76%
rename from chromeos/components/mojo_service_manager/mojom/BUILD.gn
rename to chromeos/ash/components/mojo_service_manager/mojom/BUILD.gn
index 84e07e8..c63a7ee 100644
--- a/chromeos/components/mojo_service_manager/mojom/BUILD.gn
+++ b/chromeos/ash/components/mojo_service_manager/mojom/BUILD.gn
@@ -2,8 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
+assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //chromeos/ash")
+
 mojom("mojom") {
   sources = [ "mojo_service_manager.mojom" ]
   public_deps = [ "//mojo/public/mojom/base" ]
diff --git a/chromeos/components/mojo_service_manager/mojom/OWNERS b/chromeos/ash/components/mojo_service_manager/mojom/OWNERS
similarity index 100%
rename from chromeos/components/mojo_service_manager/mojom/OWNERS
rename to chromeos/ash/components/mojo_service_manager/mojom/OWNERS
diff --git a/chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom b/chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom
similarity index 100%
rename from chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom
rename to chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn b/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
index 4a673a14..f617f4a 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
+++ b/chromeos/ash/services/cros_healthd/private/cpp/BUILD.gn
@@ -14,7 +14,7 @@
   ]
   deps = [
     "//base",
-    "//chromeos/components/mojo_service_manager",
+    "//chromeos/ash/components/mojo_service_manager",
     "//content/public/browser:browser",
     "//ui/events/devices",
     "//ui/events/ozone/evdev:event_device_info",
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
index 0f2b252..87c22a1 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
+++ b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.cc
@@ -12,7 +12,7 @@
 #include "base/notreached.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chromeos/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/cros_system_api/mojo/service_constants.h"
diff --git a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
index 4015229..54d1394d 100644
--- a/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
+++ b/chromeos/ash/services/cros_healthd/private/cpp/data_collector.h
@@ -5,8 +5,8 @@
 #ifndef CHROMEOS_ASH_SERVICES_CROS_HEALTHD_PRIVATE_CPP_DATA_COLLECTOR_H_
 #define CHROMEOS_ASH_SERVICES_CROS_HEALTHD_PRIVATE_CPP_DATA_COLLECTOR_H_
 
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "chromeos/ash/services/cros_healthd/private/mojom/cros_healthd_internal.mojom.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
 #include "chromeos/components/sensors/mojom/sensor.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/chromeos/ash/services/cros_healthd/public/cpp/BUILD.gn b/chromeos/ash/services/cros_healthd/public/cpp/BUILD.gn
index b539ffe..1752cfd 100644
--- a/chromeos/ash/services/cros_healthd/public/cpp/BUILD.gn
+++ b/chromeos/ash/services/cros_healthd/public/cpp/BUILD.gn
@@ -24,8 +24,8 @@
     "//ash/constants",
     "//base",
     "//chromeos/ash/components/dbus/cros_healthd",
+    "//chromeos/ash/components/mojo_service_manager",
     "//chromeos/ash/services/cros_healthd/public/mojom",
-    "//chromeos/components/mojo_service_manager",
     "//ui/events/ozone/evdev:event_device_info",
   ]
   if (use_real_dbus_clients) {
diff --git a/chromeos/ash/services/cros_healthd/public/cpp/service_connection.cc b/chromeos/ash/services/cros_healthd/public/cpp/service_connection.cc
index b019039..399f3d1d 100644
--- a/chromeos/ash/services/cros_healthd/public/cpp/service_connection.cc
+++ b/chromeos/ash/services/cros_healthd/public/cpp/service_connection.cc
@@ -17,8 +17,8 @@
 #include "base/sequence_checker.h"
 #include "chromeos/ash/components/dbus/cros_healthd/cros_healthd_client.h"
 #include "chromeos/ash/components/dbus/cros_healthd/fake_cros_healthd_client.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
-#include "chromeos/components/mojo_service_manager/connection.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/cros_system_api/mojo/service_constants.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
diff --git a/chromeos/ash/services/libassistant/util.cc b/chromeos/ash/services/libassistant/util.cc
index 2e700eb..6e9d53e 100644
--- a/chromeos/ash/services/libassistant/util.cc
+++ b/chromeos/ash/services/libassistant/util.cc
@@ -178,9 +178,9 @@
   Value device(Type::DICTIONARY);
   device.SetKey("board_name", Value(base::SysInfo::GetLsbReleaseBoard()));
   device.SetKey("board_revision", Value("1"));
-  device.SetKey("embedder_build_info",
-                Value(chromeos::version_loader::GetVersion(
-                    chromeos::version_loader::VERSION_FULL)));
+  absl::optional<std::string> version = chromeos::version_loader::GetVersion(
+      chromeos::version_loader::VERSION_FULL);
+  device.SetKey("embedder_build_info", Value(version.value_or("0.0.0.0")));
   device.SetKey("model_id", Value(assistant::kModelId));
   device.SetKey("model_revision", Value(1));
   config.SetKey("device", std::move(device));
diff --git a/chromeos/components/sensors/BUILD.gn b/chromeos/components/sensors/BUILD.gn
index 8e614c8..efa1eef 100644
--- a/chromeos/components/sensors/BUILD.gn
+++ b/chromeos/components/sensors/BUILD.gn
@@ -32,7 +32,7 @@
       "ash/sensor_hal_dispatcher.cc",
       "ash/sensor_hal_dispatcher.h",
     ]
-    deps += [ "//chromeos/components/mojo_service_manager" ]
+    deps += [ "//chromeos/ash/components/mojo_service_manager" ]
   }
   if (is_chromeos_lacros) {
     deps += [
@@ -70,7 +70,7 @@
       ":sensors",
       ":test_support",
       "//base/test:test_support",
-      "//chromeos/components/mojo_service_manager",
+      "//chromeos/ash/components/mojo_service_manager",
       "//chromeos/components/sensors/mojom",
       "//mojo/public/cpp/bindings",
       "//testing/gtest",
diff --git a/chromeos/components/sensors/ash/sensor_hal_dispatcher.cc b/chromeos/components/sensors/ash/sensor_hal_dispatcher.cc
index af5a774..f2c3a7d 100644
--- a/chromeos/components/sensors/ash/sensor_hal_dispatcher.cc
+++ b/chromeos/components/sensors/ash/sensor_hal_dispatcher.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/time/time.h"
-#include "chromeos/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
 #include "third_party/cros_system_api/mojo/service_constants.h"
 
 using chromeos::mojo_service_manager::mojom::ErrorOrServiceState;
diff --git a/chromeos/components/sensors/ash/sensor_hal_dispatcher.h b/chromeos/components/sensors/ash/sensor_hal_dispatcher.h
index 4c933c88..2e8ed46 100644
--- a/chromeos/components/sensors/ash/sensor_hal_dispatcher.h
+++ b/chromeos/components/sensors/ash/sensor_hal_dispatcher.h
@@ -10,7 +10,7 @@
 #include "base/component_export.h"
 #include "base/sequence_checker.h"
 #include "base/unguessable_token.h"
-#include "chromeos/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
 #include "chromeos/components/sensors/mojom/sensor.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index e4057f5..5de6d91 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -824,7 +824,7 @@
   // Opens a new window with a new tab, regardless of users' preference.
   [MinVersion=2] kOpenNewTabPageWindow = 4,
 
-  // Opens a new window with specified URLs with the primary profile.
+  // Opens a new window with specified URLs with the main profile.
   // The URLs should be set with BrowserInitParams::startup_urls.
   // Added in M96.
   [MinVersion=3] kOpenWindowWithUrls = 5,
@@ -1403,8 +1403,8 @@
 // has accidentally become a kitchen sink for different features. This was not
 // intentional.
 //
-// Next MinVersion: 70.
-// Next ID: 19
+// Next MinVersion: 71.
+// Next ID: 20
 //
 [Stable, Uuid="4e04dc16-b34c-40fd-9e3f-3c55c2c6cf91",
  RenamedFrom="crosapi.mojom.LacrosChromeService"]
@@ -1413,7 +1413,7 @@
   REMOVED_0@0() => (pending_receiver<Crosapi> receiver);
   REMOVED_2@2(BrowserInitParams params);
 
-  // Opens a new window in the browser with, currently, the last used profile.
+  // Opens a new window in the browser with, currently, the main profile.
   // The callback is called on the command execution.
   // If `should_trigger_session_restore` is true, a new window opening should be
   // treated like the start of a new session (with potential session restore,
@@ -1423,17 +1423,16 @@
     [MinVersion=10] bool incognito,
     [MinVersion=61] bool should_trigger_session_restore) => ();
 
-  // Opens a new fullscreen window in the browser with, currently, the last
-  // used profile. The only tab will be navigated to the given `url` once the
-  // window is launched. The callback is called on the command execution.
-  // NOTE: This method is used by Chrome OS web Kiosk session only. The behavior
-  // may change and it shouldn't be used by anybody else.
+  // Opens a new fullscreen window in the browser with, currently, the main
+  // profile. The only tab will be navigated to the given `url` once the window
+  // is launched. The callback is called on the command execution. NOTE: This
+  // method is used by Chrome OS web Kiosk session only. The behavior may change
+  // and it shouldn't be used by anybody else.
   // Added in M96.
   [MinVersion=11]
   NewFullscreenWindow@9(url.mojom.Url url) => (CreationResult result);
 
-  // Opens a new window in the browser and transfers the given tab (or group)
-  // to it.
+  // Transfers the given tab (or group) to a new window with the same profile.
   // NOTE: This method is used by Chrome OS WebUI in tablet mode as a response
   // to a drag'n drop operation from the user.
   [MinVersion=13]
@@ -1447,12 +1446,12 @@
   [MinVersion=63]
   NewGuestWindow@14() => ();
 
-  // Opens a new tab in the browser with, currently, the last used profile.
-  // This may open a new window, if there's no window. If a new browser is
-  // opened and `should_trigger_session_restore` is set, the new browser will
-  // open with the profile's session startup pref. If this is not set the
-  // browser will open without considering the user's session startup pref.
-  // Please see also the Chrome's NewTab command for details.
+  // Opens a new tab in the browser with, currently, the main profile. This may
+  // open a new window, if there's no window. If a new browser is opened and
+  // `should_trigger_session_restore` is set, the new browser will open with the
+  // profile's session startup pref. If this is not set the browser will open
+  // without considering the user's session startup pref. Please see also the
+  // Chrome's NewTab command for details.
   // The callback is called on the command execution.
   // This is designed to be equivalent of CTRL+T behavior. By default, this
   // opens a new-tab-page, but extensions may override the behavior.
@@ -1460,8 +1459,8 @@
   [MinVersion=10]
   NewTab@7([MinVersion=68] bool should_trigger_session_restore) => ();
 
-  // Opens the specified URL in the browser. This opens a new tab and loads
-  // the page at specified URL.
+  // Opens the specified URL in the browser with, currently, the main profile.
+  // This opens a new tab and loads the page at specified URL.
   // This is designed to handle opening URL requests from other OS parts,
   // such as web-page opening request from ash features, or Crostini/ARC apps.
   // Added in M96.
@@ -1470,8 +1469,7 @@
              [MinVersion=62] OpenUrlParams? params) => ();
 
   // Restores a tab (or a window) recently closed in the browser with,
-  // currently, the last used profile. Please see also RestoreTab command
-  // for details.
+  // currently, main profile. Please see also RestoreTab command for details.
   // The callback is called on the command execution.
   // Added in M91.
   [MinVersion=10]
@@ -1521,7 +1519,7 @@
 
   // Opens lacros with a full restore (all previously open windows from the
   // previous lacros session). This must be called before a browser has been
-  // created as this will update the state of the last opened profiles.
+  // created as this will update the state of the main profile.
   // If `skip_crash_restore` is true full restore will restore the previous
   // session, bypassing the typical crash restore flow (i.e. the crash restore
   // bubble anchored to the browser's app menu button).
@@ -1538,6 +1536,12 @@
   // for that extension.
   [MinVersion=67]
   UpdateComponentPolicy@18(map<crosapi.mojom.PolicyNamespace, mojo_base.mojom.Value> component_policy);
+
+  // Opens a new tab in the browser with, currently, the main profile.
+  // If a new window needs to be created, it will open with the profile's
+  // session startup pref and no new tab will be added.
+  [MinVersion=70]
+  Launch@19() => ();
 };
 
 // TODO(crbug.com/1180712): move to its own file. Currently due to circular
diff --git a/chromeos/version/version_loader.cc b/chromeos/version/version_loader.cc
index faba6ac..f64ca91 100644
--- a/chromeos/version/version_loader.cc
+++ b/chromeos/version/version_loader.cc
@@ -49,13 +49,13 @@
 
 }  // namespace
 
-std::string GetVersion(VersionFormat format) {
+absl::optional<std::string> GetVersion(VersionFormat format) {
   std::string version;
   std::string key = (format == VERSION_FULL ? kFullVersionKey : kVersionKey);
   if (!base::SysInfo::GetLsbReleaseValue(key, &version)) {
     LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
         << "No LSB version key: " << key;
-    version = "0.0.0.0";
+    return absl::nullopt;
   }
   if (format == VERSION_SHORT_WITH_DATE) {
     base::Time::Exploded ctime;
diff --git a/chromeos/version/version_loader.h b/chromeos/version/version_loader.h
index 90f0d4a..991de7e 100644
--- a/chromeos/version/version_loader.h
+++ b/chromeos/version/version_loader.h
@@ -22,9 +22,10 @@
 // Gets the version.
 // If |full_version| is true version string with extra info is extracted,
 // otherwise it's in short format x.x.xx.x.
+// If not found returns absl::nullopt.
 // May block.
 COMPONENT_EXPORT(CHROMEOS_VERSION)
-std::string GetVersion(VersionFormat format);
+absl::optional<std::string> GetVersion(VersionFormat format);
 
 // Gets the ARC version.
 // May block.
diff --git a/components/app_restore/app_restore_data.cc b/components/app_restore/app_restore_data.cc
index c324448..a9575d8 100644
--- a/components/app_restore/app_restore_data.cc
+++ b/components/app_restore/app_restore_data.cc
@@ -64,53 +64,52 @@
   return base::Value(base::NumberToString(number));
 }
 
-// Gets bool value from base::DictionaryValue, e.g. { "key": true } returns
+// Gets bool value from base::Value::Dict, e.g. { "key": true } returns
 // true.
-absl::optional<bool> GetBoolValueFromDict(const base::DictionaryValue& dict,
+absl::optional<bool> GetBoolValueFromDict(const base::Value::Dict& dict,
                                           const std::string& key_name) {
-  return dict.FindBoolKey(key_name);
+  return dict.FindBool(key_name);
 }
 
-// Gets int value from base::DictionaryValue, e.g. { "key": 100 } returns 100.
-absl::optional<int32_t> GetIntValueFromDict(const base::DictionaryValue& dict,
+// Gets int value from base::Value::Dict, e.g. { "key": 100 } returns 100.
+absl::optional<int32_t> GetIntValueFromDict(const base::Value::Dict& dict,
                                             const std::string& key_name) {
-  return dict.FindIntKey(key_name);
+  return dict.FindInt(key_name);
 }
 
-// Gets uint32_t value from base::DictionaryValue, e.g. { "key": "123" } returns
+// Gets uint32_t value from base::Value::Dict, e.g. { "key": "123" } returns
 // 123.
-absl::optional<uint32_t> GetUIntValueFromDict(const base::DictionaryValue& dict,
+absl::optional<uint32_t> GetUIntValueFromDict(const base::Value::Dict& dict,
                                               const std::string& key_name) {
   uint32_t result = 0;
-  if (!dict.FindKey(key_name) ||
-      !base::StringToUint(dict.FindStringKey(key_name)->c_str(), &result)) {
+  if (!dict.contains(key_name) ||
+      !base::StringToUint(dict.FindString(key_name)->c_str(), &result)) {
     return absl::nullopt;
   }
   return result;
 }
 
 absl::optional<std::string> GetStringValueFromDict(
-    const base::DictionaryValue& dict,
+    const base::Value::Dict& dict,
     const std::string& key_name) {
-  const std::string* value = dict.FindStringKey(key_name);
+  const std::string* value = dict.FindString(key_name);
   return value ? absl::optional<std::string>(*value) : absl::nullopt;
 }
 
 absl::optional<std::u16string> GetU16StringValueFromDict(
-    const base::DictionaryValue& dict,
+    const base::Value::Dict& dict,
     const std::string& key_name) {
   std::u16string result;
-  const std::string* value = dict.FindStringKey(key_name);
+  const std::string* value = dict.FindString(key_name);
   if (!value || !base::UTF8ToUTF16(value->c_str(), value->length(), &result))
     return absl::nullopt;
   return result;
 }
 
-// Gets display id from base::DictionaryValue, e.g. { "display_id": "22000000" }
+// Gets display id from base::Value::Dict, e.g. { "display_id": "22000000" }
 // returns 22000000.
-absl::optional<int64_t> GetDisplayIdFromDict(
-    const base::DictionaryValue& dict) {
-  const std::string* display_id_str = dict.FindStringKey(kDisplayIdKey);
+absl::optional<int64_t> GetDisplayIdFromDict(const base::Value::Dict& dict) {
+  const std::string* display_id_str = dict.FindString(kDisplayIdKey);
   int64_t display_id_value;
   if (display_id_str &&
       base::StringToInt64(*display_id_str, &display_id_value)) {
@@ -122,15 +121,14 @@
 
 // Gets urls from the dictionary value.
 absl::optional<std::vector<GURL>> GetUrlsFromDict(
-    const base::DictionaryValue& dict) {
-  const base::Value* urls_path_value = dict.FindListKey(kUrlsKey);
-  if (!urls_path_value || !urls_path_value->is_list() ||
-      urls_path_value->GetList().empty()) {
+    const base::Value::Dict& dict) {
+  const base::Value::List* urls_path_value = dict.FindList(kUrlsKey);
+  if (!urls_path_value || urls_path_value->empty()) {
     return absl::nullopt;
   }
 
   std::vector<GURL> url_paths;
-  for (const auto& item : urls_path_value->GetList()) {
+  for (const auto& item : *urls_path_value) {
     if (item.GetString().empty())
       continue;
     GURL url(item.GetString());
@@ -142,18 +140,17 @@
   return url_paths;
 }
 
-// Gets std::vector<base::FilePath> from base::DictionaryValue, e.g.
+// Gets std::vector<base::FilePath> from base::Value::Dict, e.g.
 // {"file_paths": { "aa.cc", "bb.h", ... }} returns
 // std::vector<base::FilePath>{"aa.cc", "bb.h", ...}.
 absl::optional<std::vector<base::FilePath>> GetFilePathsFromDict(
-    const base::DictionaryValue& dict) {
-  const base::Value* file_paths_value = dict.FindListKey(kFilePathsKey);
-  if (!file_paths_value || !file_paths_value->is_list() ||
-      file_paths_value->GetList().empty())
+    const base::Value::Dict& dict) {
+  const base::Value::List* file_paths_value = dict.FindList(kFilePathsKey);
+  if (!file_paths_value || file_paths_value->empty())
     return absl::nullopt;
 
   std::vector<base::FilePath> file_paths;
-  for (const auto& item : file_paths_value->GetList()) {
+  for (const auto& item : *file_paths_value) {
     if (item.GetString().empty())
       continue;
     file_paths.push_back(base::FilePath(item.GetString()));
@@ -164,16 +161,15 @@
 
 // Gets gfx::Size from base::Value, e.g. { 100, 300 } returns
 // gfx::Size(100, 300).
-absl::optional<gfx::Size> GetSizeFromDict(const base::DictionaryValue& dict,
+absl::optional<gfx::Size> GetSizeFromDict(const base::Value::Dict& dict,
                                           const std::string& key_name) {
-  const base::Value* size_value = dict.FindListKey(key_name);
-  if (!size_value || !size_value->is_list() ||
-      size_value->GetList().size() != 2) {
+  const base::Value::List* size_value = dict.FindList(key_name);
+  if (!size_value || size_value->size() != 2) {
     return absl::nullopt;
   }
 
   std::vector<int> size;
-  for (const auto& item : size_value->GetList())
+  for (const auto& item : *size_value)
     size.push_back(item.GetInt());
 
   return gfx::Size(size[0], size[1]);
@@ -181,15 +177,14 @@
 
 // Gets gfx::Rect from base::Value, e.g. { 0, 100, 200, 300 } returns
 // gfx::Rect(0, 100, 200, 300).
-absl::optional<gfx::Rect> GetBoundsRectFromDict(
-    const base::DictionaryValue& dict,
-    const std::string& key_name) {
-  const base::Value* rect_value = dict.FindListKey(key_name);
-  if (!rect_value || !rect_value->is_list() || rect_value->GetList().empty())
+absl::optional<gfx::Rect> GetBoundsRectFromDict(const base::Value::Dict& dict,
+                                                const std::string& key_name) {
+  const base::Value::List* rect_value = dict.FindList(key_name);
+  if (!rect_value || rect_value->empty())
     return absl::nullopt;
 
   std::vector<int> rect;
-  for (const auto& item : rect_value->GetList())
+  for (const auto& item : *rect_value)
     rect.push_back(item.GetInt());
 
   if (rect.size() != 4)
@@ -198,21 +193,21 @@
   return gfx::Rect(rect[0], rect[1], rect[2], rect[3]);
 }
 
-// Gets WindowStateType from base::DictionaryValue, e.g. { "window_state_type":
+// Gets WindowStateType from base::Value::Dict, e.g. { "window_state_type":
 // 2 } returns WindowStateType::kMinimized.
 absl::optional<chromeos::WindowStateType> GetWindowStateTypeFromDict(
-    const base::DictionaryValue& dict) {
-  return dict.FindKey(kWindowStateTypeKey)
+    const base::Value::Dict& dict) {
+  return dict.Find(kWindowStateTypeKey)
              ? absl::make_optional(static_cast<chromeos::WindowStateType>(
-                   dict.FindIntKey(kWindowStateTypeKey).value()))
+                   dict.FindInt(kWindowStateTypeKey).value()))
              : absl::nullopt;
 }
 
 absl::optional<ui::WindowShowState> GetPreMinimizedShowStateTypeFromDict(
-    const base::DictionaryValue& dict) {
-  return dict.FindKey(kPreMinimizedShowStateTypeKey)
+    const base::Value::Dict& dict) {
+  return dict.Find(kPreMinimizedShowStateTypeKey)
              ? absl::make_optional(static_cast<ui::WindowShowState>(
-                   dict.FindIntKey(kPreMinimizedShowStateTypeKey).value()))
+                   dict.FindInt(kPreMinimizedShowStateTypeKey).value()))
              : absl::nullopt;
 }
 
@@ -221,8 +216,8 @@
 AppRestoreData::AppRestoreData() = default;
 
 AppRestoreData::AppRestoreData(base::Value&& value) {
-  base::DictionaryValue* data_dict = nullptr;
-  if (!value.is_dict() || !value.GetAsDictionary(&data_dict) || !data_dict) {
+  const base::Value::Dict* data_dict = value.GetIfDict();
+  if (!data_dict) {
     DVLOG(0) << "Fail to parse app restore data. "
              << "Cannot find the app restore data dict.";
     return;
@@ -254,9 +249,9 @@
   primary_color = GetUIntValueFromDict(*data_dict, kPrimaryColorKey);
   status_bar_color = GetUIntValueFromDict(*data_dict, kStatusBarColorKey);
 
-  if (data_dict->FindKey(kIntentKey)) {
-    intent = apps_util::ConvertValueToIntent(
-        std::move(*data_dict->FindDictKey(kIntentKey)));
+  const base::Value::Dict* intent_value = data_dict->FindDict(kIntentKey);
+  if (intent_value) {
+    intent = apps_util::ConvertDictToIntent(*intent_value);
   }
 }
 
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 9ca4e2c..5450d0a 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -590,7 +590,7 @@
   const auto* key_info_list =
       request_options.FindKeyOfType("key_info", base::Value::Type::LIST);
   DCHECK(key_info_list);
-  for (const base::Value& key_info : key_info_list->GetListDeprecated()) {
+  for (const base::Value& key_info : key_info_list->GetList()) {
     options->allow_credentials.push_back(ParseCredentialDescriptor(key_info));
   }
 
@@ -632,8 +632,7 @@
   const auto* identifier_list = creation_options.FindKeyOfType(
       "algorithm_identifier", base::Value::Type::LIST);
   if (identifier_list) {
-    for (const base::Value& algorithm_identifier :
-         identifier_list->GetListDeprecated()) {
+    for (const base::Value& algorithm_identifier : identifier_list->GetList()) {
       device::PublicKeyCredentialParams::CredentialInfo parameter;
       parameter.type = device::CredentialType::kPublicKey;
       parameter.algorithm = algorithm_identifier.GetInt();
@@ -669,8 +668,7 @@
   const auto* excluded_keys_list =
       creation_options.FindKeyOfType("key_info", base::Value::Type::LIST);
   if (excluded_keys_list) {
-    for (const base::Value& key_info :
-         excluded_keys_list->GetListDeprecated()) {
+    for (const base::Value& key_info : excluded_keys_list->GetList()) {
       options->exclude_credentials.push_back(
           ParseCredentialDescriptor(key_info));
     }
@@ -690,8 +688,8 @@
   base::flat_set<device::FidoTransportProtocol> authenticator_transports;
   const auto* transports = key_info.FindKeyOfType(
       "authenticator_transport_support", base::Value::Type::LIST);
-  if (transports && !transports->GetListDeprecated().empty()) {
-    for (const base::Value& transport_type : transports->GetListDeprecated()) {
+  if (transports && !transports->GetList().empty()) {
+    for (const base::Value& transport_type : transports->GetList()) {
       absl::optional<device::FidoTransportProtocol> protocol =
           device::ConvertToFidoTransportProtocol(
               base::ToLowerASCII(transport_type.GetString()));
@@ -757,10 +755,10 @@
   const auto* key_info_list =
       request_options.FindKeyOfType("key_info", base::Value::Type::LIST);
 
-  if (key_info_list->GetListDeprecated().empty())
+  if (key_info_list->GetList().empty())
     return false;
 
-  for (const base::Value& key_info : key_info_list->GetListDeprecated()) {
+  for (const base::Value& key_info : key_info_list->GetList()) {
     if (!key_info.is_dict() || !key_info.FindStringKey("credential_id"))
       return false;
   }
diff --git a/components/autofill/core/browser/payments/legal_message_line.cc b/components/autofill/core/browser/payments/legal_message_line.cc
index b4556747..7b14a308 100644
--- a/components/autofill/core/browser/payments/legal_message_line.cc
+++ b/components/autofill/core/browser/payments/legal_message_line.cc
@@ -85,9 +85,9 @@
       legal_message.FindKeyOfType("line", base::Value::Type::LIST);
   if (lines_list) {
     LegalMessageLines lines;
-    lines.reserve(lines_list->GetListDeprecated().size());
-    for (const base::Value& single_line : lines_list->GetListDeprecated()) {
-      lines.emplace_back(LegalMessageLine());
+    lines.reserve(lines_list->GetList().size());
+    for (const base::Value& single_line : lines_list->GetList()) {
+      lines.emplace_back();
       if (!single_line.is_dict() ||
           !lines.back().ParseLine(single_line, escape_apostrophes))
         return false;
@@ -112,8 +112,8 @@
   const base::Value* template_parameters =
       line.FindKeyOfType("template_parameter", base::Value::Type::LIST);
   if (template_parameters) {
-    base::Value::ConstListView template_parameters_view =
-        template_parameters->GetListDeprecated();
+    const base::Value::List& template_parameters_view =
+        template_parameters->GetList();
     display_texts.reserve(template_parameters_view.size());
     links_.reserve(template_parameters_view.size());
 
diff --git a/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc b/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
index 1041b44..500725b 100644
--- a/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.cc
@@ -78,8 +78,7 @@
   const auto* fido_eligible_card_ids =
       response.FindKeyOfType("fido_eligible_card_id", base::Value::Type::LIST);
   if (fido_eligible_card_ids) {
-    for (const base::Value& result :
-         fido_eligible_card_ids->GetListDeprecated()) {
+    for (const base::Value& result : fido_eligible_card_ids->GetList()) {
       unmask_details_.fido_eligible_card_ids.insert(result.GetString());
     }
   }
diff --git a/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc b/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc
index b03170e..64092597 100644
--- a/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/migrate_cards_request.cc
@@ -98,7 +98,7 @@
 
   save_result_ =
       std::make_unique<std::unordered_map<std::string, std::string>>();
-  for (const base::Value& result : found_list->GetListDeprecated()) {
+  for (const base::Value& result : found_list->GetList()) {
     if (result.is_dict()) {
       const std::string* unique_id = result.FindStringKey("unique_id");
       const std::string* status = result.FindStringKey("status");
diff --git a/components/autofill/core/browser/payments/payments_requests/payments_request.cc b/components/autofill/core/browser/payments/payments_requests/payments_request.cc
index e591803..f54be23 100644
--- a/components/autofill/core/browser/payments/payments_requests/payments_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/payments_request.cc
@@ -73,7 +73,7 @@
                          address_lines);
   AppendStringIfNotEmpty(profile, ADDRESS_HOME_LINE3, app_locale,
                          address_lines);
-  if (!address_lines.GetListDeprecated().empty())
+  if (!address_lines.GetList().empty())
     postal_address.SetKey("address_line", std::move(address_lines));
 
   SetStringIfNotEmpty(profile, ADDRESS_HOME_CITY, app_locale, "locality_name",
diff --git a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
index 4ad2fa8..d8ffb15 100644
--- a/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
+++ b/components/autofill/core/browser/payments/payments_requests/unmask_card_request.cc
@@ -227,7 +227,7 @@
   if (challenge_option_list) {
     std::vector<CardUnmaskChallengeOption> card_unmask_challenge_options;
     for (const base::Value& challenge_option :
-         challenge_option_list->GetListDeprecated()) {
+         challenge_option_list->GetList()) {
       CardUnmaskChallengeOption parsed_challenge_option =
           ParseCardUnmaskChallengeOption(challenge_option);
       // Only return successfully parsed challenge option.
diff --git a/components/browsing_data/core/counters/history_counter.cc b/components/browsing_data/core/counters/history_counter.cc
index 40ce800..8fd1935d 100644
--- a/components/browsing_data/core/counters/history_counter.cc
+++ b/components/browsing_data/core/counters/history_counter.cc
@@ -154,7 +154,7 @@
   if (!result)
     has_synced_visits_ = true;
   else if (const base::Value* events = result->FindListKey("event"))
-    has_synced_visits_ = !events->GetListDeprecated().empty();
+    has_synced_visits_ = !events->GetList().empty();
   else
     has_synced_visits_ = false;
   web_counting_finished_ = true;
diff --git a/components/commerce/core/mock_shopping_service.cc b/components/commerce/core/mock_shopping_service.cc
index a582e90..3e4265e 100644
--- a/components/commerce/core/mock_shopping_service.cc
+++ b/components/commerce/core/mock_shopping_service.cc
@@ -100,4 +100,6 @@
   unsubscribe_callback_value_ = unsubscribe_should_succeed;
 }
 
+void MockShoppingService::ScheduleSavedProductUpdate() {}
+
 }  // namespace commerce
\ No newline at end of file
diff --git a/components/commerce/core/mock_shopping_service.h b/components/commerce/core/mock_shopping_service.h
index a4a84f3..f2a0440 100644
--- a/components/commerce/core/mock_shopping_service.h
+++ b/components/commerce/core/mock_shopping_service.h
@@ -38,6 +38,7 @@
   void Unsubscribe(
       std::unique_ptr<std::vector<CommerceSubscription>> subscriptions,
       base::OnceCallback<void(bool)> callback) override;
+  void ScheduleSavedProductUpdate() override;
 
   void SetResponseForGetProductInfoForUrl(
       absl::optional<commerce::ProductInfo> product_info);
diff --git a/components/commerce/core/shopping_service.cc b/components/commerce/core/shopping_service.cc
index 6ffde1f0..44a3fcd 100644
--- a/components/commerce/core/shopping_service.cc
+++ b/components/commerce/core/shopping_service.cc
@@ -651,6 +651,10 @@
   }
 }
 
+void ShoppingService::ScheduleSavedProductUpdate() {
+  bookmark_update_manager_->ScheduleUpdate();
+}
+
 base::WeakPtr<ShoppingService> ShoppingService::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/components/commerce/core/shopping_service.h b/components/commerce/core/shopping_service.h
index d23a68c4..8a846fd 100644
--- a/components/commerce/core/shopping_service.h
+++ b/components/commerce/core/shopping_service.h
@@ -210,6 +210,10 @@
   // Fetch users' pref from server on whether to receive price tracking emails.
   void FetchPriceEmailPref();
 
+  // Schedule an update for saved product bookmarks using
+  // |bookmark_update_manager_|.
+  virtual void ScheduleSavedProductUpdate();
+
   // Get a weak pointer for this service instance.
   base::WeakPtr<ShoppingService> AsWeakPtr();
 
diff --git a/components/commerce/core/webui/shopping_list_handler.cc b/components/commerce/core/webui/shopping_list_handler.cc
index 57bb31b..f90adfa 100644
--- a/components/commerce/core/webui/shopping_list_handler.cc
+++ b/components/commerce/core/webui/shopping_list_handler.cc
@@ -77,6 +77,7 @@
       shopping_service_(shopping_service),
       locale_(locale) {
   scoped_observation_.Observe(bookmark_model);
+  shopping_service_->ScheduleSavedProductUpdate();
 }
 
 ShoppingListHandler::~ShoppingListHandler() = default;
diff --git a/components/domain_reliability/context_unittest.cc b/components/domain_reliability/context_unittest.cc
index 6b6762d..fd0428ee 100644
--- a/components/domain_reliability/context_unittest.cc
+++ b/components/domain_reliability/context_unittest.cc
@@ -110,9 +110,9 @@
   if (!report || !report->is_dict())
     return false;
   const Value* entries = report->FindListKey("entries");
-  if (!entries || index >= entries->GetListDeprecated().size())
+  if (!entries || index >= entries->GetList().size())
     return false;
-  const Value& entry = entries->GetListDeprecated()[index];
+  const Value& entry = entries->GetList()[index];
   if (!entry.is_dict())
     return false;
   *entry_out = &entry;
diff --git a/components/history/core/browser/top_sites_database.cc b/components/history/core/browser/top_sites_database.cc
index d7a5035..7596b37 100644
--- a/components/history/core/browser/top_sites_database.cc
+++ b/components/history/core/browser/top_sites_database.cc
@@ -11,13 +11,16 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/sequence_checker.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/top_sites.h"
 #include "sql/database.h"
+#include "sql/meta_table.h"
 #include "sql/recovery.h"
 #include "sql/statement.h"
 #include "sql/transaction.h"
@@ -208,21 +211,22 @@
   RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED);
 }
 
-void DatabaseErrorCallback(sql::Database* db,
-                           const base::FilePath& db_path,
-                           int extended_error,
-                           sql::Statement* stmt) {
-  // TODO(shess): Assert that this is running on a safe thread.  AFAICT, should
-  // be the history thread, but at this level I can't see how to reach that.
+}  // namespace
+
+void TopSitesDatabase::DatabaseErrorCallback(const base::FilePath& db_path,
+                                             int extended_error,
+                                             sql::Statement* stmt) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(db_);
 
   // Attempt to recover corrupt databases.
   if (sql::Recovery::ShouldRecover(extended_error)) {
     // Prevent reentrant calls.
-    db->reset_error_callback();
+    db_->reset_error_callback();
 
     // After this call, the `db` handle is poisoned so that future calls will
     // return errors until the handle is re-opened.
-    RecoverAndFixup(db, db_path);
+    RecoverAndFixup(db_.get(), db_path);
 
     // The DLOG(FATAL) below is intended to draw immediate attention to errors
     // in newly-written code.  Database corruption is generally a result of OS
@@ -249,32 +253,23 @@
 
   // The default handling is to assert on debug and to ignore on release.
   if (!sql::Database::IsExpectedSqliteError(extended_error))
-    DLOG(FATAL) << db->GetErrorMessage();
+    DLOG(FATAL) << db_->GetErrorMessage();
 }
 
-std::unique_ptr<sql::Database> CreateDB(const base::FilePath& db_name) {
-  // Settings copied from FaviconDatabase.
-  auto db = std::make_unique<sql::Database>(sql::DatabaseOptions{
-      .exclusive_locking = true, .page_size = 4096, .cache_size = 32});
-  db->set_histogram_tag("TopSites");
-  db->set_error_callback(
-      base::BindRepeating(&DatabaseErrorCallback, db.get(), db_name));
-
-  if (!db->Open(db_name))
-    return nullptr;
-  return db;
-}
-
-}  // namespace
-
 // static
 const int TopSitesDatabase::kRankOfNonExistingURL = -2;
 
-TopSitesDatabase::TopSitesDatabase() = default;
+TopSitesDatabase::TopSitesDatabase() {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
 
-TopSitesDatabase::~TopSitesDatabase() = default;
+TopSitesDatabase::~TopSitesDatabase() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 bool TopSitesDatabase::Init(const base::FilePath& db_name) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // Retry failed InitImpl() in case the recovery system fixed things.
   // TODO(shess): Instrument to figure out if there are any persistent failure
   // cases which do not resolve themselves.
@@ -284,7 +279,6 @@
     if (InitImpl(db_name))
       return true;
 
-    meta_table_.Reset();
     db_.reset();
   }
   return false;
@@ -293,8 +287,15 @@
 bool TopSitesDatabase::InitImpl(const base::FilePath& db_name) {
   const bool file_existed = base::PathExists(db_name);
 
-  db_ = CreateDB(db_name);
-  if (!db_)
+  // Settings copied from FaviconDatabase.
+  db_ = std::make_unique<sql::Database>(sql::DatabaseOptions{
+      .exclusive_locking = true, .page_size = 4096, .cache_size = 32});
+  db_->set_histogram_tag("TopSites");
+  db_->set_error_callback(
+      base::BindRepeating(&TopSitesDatabase::DatabaseErrorCallback,
+                          base::Unretained(this), db_name));
+
+  if (!db_->Open(db_name))
     return false;
 
   // An older version had data with no meta table.  Deprecate by razing.
@@ -318,14 +319,16 @@
   if (!transaction.Begin())
     return false;
 
-  if (!meta_table_.Init(db_.get(), kVersionNumber, kVersionNumber))
+  sql::MetaTable meta_table;
+
+  if (!meta_table.Init(db_.get(), kVersionNumber, kVersionNumber))
     return false;
 
   if (!InitTables(db_.get()))
     return false;
 
   // Version check.
-  if (meta_table_.GetVersionNumber() != kVersionNumber)
+  if (meta_table.GetVersionNumber() != kVersionNumber)
     return false;
 
   // Initialization is complete.
@@ -333,6 +336,8 @@
 }
 
 void TopSitesDatabase::ApplyDelta(const TopSitesDelta& delta) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   sql::Transaction transaction(db_.get());
   // TODO: consider returning early if `Begin()` returns false.
   std::ignore = transaction.Begin();
@@ -352,6 +357,8 @@
 }
 
 void TopSitesDatabase::GetSites(MostVisitedURLList* urls) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   sql::Statement statement(
       db_->GetCachedStatement(SQL_FROM_HERE,
                               "SELECT url,title "
@@ -507,4 +514,20 @@
   return delete_statement.Run();
 }
 
+sql::Database* TopSitesDatabase::db_for_testing() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return db_.get();
+}
+
+int TopSitesDatabase::GetURLRankForTesting(const MostVisitedURL& url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return GetURLRank(url);
+}
+
+bool TopSitesDatabase::RemoveURLNoTransactionForTesting(
+    const MostVisitedURL& url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return RemoveURLNoTransaction(url);
+}
+
 }  // namespace history
diff --git a/components/history/core/browser/top_sites_database.h b/components/history/core/browser/top_sites_database.h
index b191c98..cf8bbe8 100644
--- a/components/history/core/browser/top_sites_database.h
+++ b/components/history/core/browser/top_sites_database.h
@@ -7,9 +7,9 @@
 
 #include <memory>
 
-#include "base/gtest_prod_util.h"
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
 #include "components/history/core/browser/history_types.h"
-#include "sql/meta_table.h"
 
 namespace base {
 class FilePath;
@@ -17,12 +17,16 @@
 
 namespace sql {
 class Database;
+class Statement;
 }  // namespace sql
 
 namespace history {
 
 class TopSitesDatabase {
  public:
+  // Rank used to indicate that a URL is not stored in the database.
+  static const int kRankOfNonExistingURL;
+
   TopSitesDatabase();
 
   TopSitesDatabase(const TopSitesDatabase&) = delete;
@@ -41,53 +45,57 @@
   // WARNING: clears input argument.
   void GetSites(MostVisitedURLList* urls);
 
+  sql::Database* db_for_testing();
+
+  int GetURLRankForTesting(const MostVisitedURL& url);
+
+  bool RemoveURLNoTransactionForTesting(const MostVisitedURL& url);
+
  private:
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Version1);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Version2);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Version3);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Version4);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Recovery1);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Recovery2);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Recovery3to4_CorruptIndex);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest, Recovery4_CorruptIndex);
-  FRIEND_TEST_ALL_PREFIXES(TopSitesDatabaseTest,
-                           Recovery4_CorruptIndexAndLostRow);
-
-  // Rank used to indicate that a URL is not stored in the database.
-  static const int kRankOfNonExistingURL;
-
   // Sets a top site for the URL. `new_rank` is the position of the URL in the
   // list of top sites, zero-based.
   // If the URL is not in the table, adds it. If it is, updates its rank and
   // shifts the ranks of other URLs if necessary. Should be called within an
   // open transaction.
-  void SetSiteNoTransaction(const MostVisitedURL& url, int new_rank);
+  void SetSiteNoTransaction(const MostVisitedURL& url, int new_rank)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Adds a new URL to the database.
-  void AddSite(const MostVisitedURL& url, int new_rank);
+  void AddSite(const MostVisitedURL& url, int new_rank)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Updates title and redirects of a URL that's already in the database.
   // Returns true if the database query succeeds.
-  bool UpdateSite(const MostVisitedURL& url);
+  bool UpdateSite(const MostVisitedURL& url)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Returns `url`'s current rank or kRankOfNonExistingURL if not present.
-  int GetURLRank(const MostVisitedURL& url);
+  int GetURLRank(const MostVisitedURL& url)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Sets the rank for a given URL. The URL must be in the database. Should be
   // called within an open transaction.
-  void UpdateSiteRankNoTransaction(const MostVisitedURL& url, int new_rank);
+  void UpdateSiteRankNoTransaction(const MostVisitedURL& url, int new_rank)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Removes the record for this URL. Returns false iff there is a failure in
   // running the statement. Should be called within an open transaction.
-  bool RemoveURLNoTransaction(const MostVisitedURL& url);
+  bool RemoveURLNoTransaction(const MostVisitedURL& url)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   // Helper function to implement internals of Init().  This allows
   // Init() to retry in case of failure, since some failures will
   // invoke recovery code.
-  bool InitImpl(const base::FilePath& db_name);
+  bool InitImpl(const base::FilePath& db_name)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
 
-  std::unique_ptr<sql::Database> db_;
-  sql::MetaTable meta_table_;
+  void DatabaseErrorCallback(const base::FilePath& db_path,
+                             int extended_error,
+                             sql::Statement* stmt);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  std::unique_ptr<sql::Database> db_ GUARDED_BY_CONTEXT(sequence_checker_);
 };
 
 }  // namespace history
diff --git a/components/history/core/browser/top_sites_database_unittest.cc b/components/history/core/browser/top_sites_database_unittest.cc
index 40acca9e..52ccc8b7 100644
--- a/components/history/core/browser/top_sites_database_unittest.cc
+++ b/components/history/core/browser/top_sites_database_unittest.cc
@@ -82,8 +82,8 @@
 
   TopSitesDatabase db;
   ASSERT_TRUE(db.Init(file_name_));
-  VerifyTablesAndColumns(db.db_.get());
-  VerifyDatabaseEmpty(db.db_.get());
+  VerifyTablesAndColumns(db.db_for_testing());
+  VerifyDatabaseEmpty(db.db_for_testing());
 }
 
 // Version 2 is deprecated, the resulting schema should be current,
@@ -93,8 +93,8 @@
 
   TopSitesDatabase db;
   ASSERT_TRUE(db.Init(file_name_));
-  VerifyTablesAndColumns(db.db_.get());
-  VerifyDatabaseEmpty(db.db_.get());
+  VerifyTablesAndColumns(db.db_for_testing());
+  VerifyDatabaseEmpty(db.db_for_testing());
 }
 
 // Version 3 is deprecated, the resulting schema should be current,
@@ -104,8 +104,8 @@
 
   TopSitesDatabase db;
   ASSERT_TRUE(db.Init(file_name_));
-  VerifyTablesAndColumns(db.db_.get());
-  VerifyDatabaseEmpty(db.db_.get());
+  VerifyTablesAndColumns(db.db_for_testing());
+  VerifyDatabaseEmpty(db.db_for_testing());
 }
 
 TEST_F(TopSitesDatabaseTest, Version4) {
@@ -114,7 +114,7 @@
   TopSitesDatabase db;
   ASSERT_TRUE(db.Init(file_name_));
 
-  VerifyTablesAndColumns(db.db_.get());
+  VerifyTablesAndColumns(db.db_for_testing());
 
   // Basic operational check.
   MostVisitedURLList urls;
@@ -122,9 +122,9 @@
   ASSERT_EQ(3u, urls.size());
   EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
 
-  sql::Transaction transaction(db.db_.get());
+  sql::Transaction transaction(db.db_for_testing());
   ASSERT_TRUE(transaction.Begin());
-  ASSERT_TRUE(db.RemoveURLNoTransaction(urls[1]));
+  ASSERT_TRUE(db.RemoveURLNoTransactionForTesting(urls[1]));
   transaction.Commit();
 
   db.GetSites(&urls);
@@ -161,8 +161,8 @@
       ASSERT_TRUE(db.Init(file_name_));
       EXPECT_TRUE(expecter.SawExpectedErrors());
     }
-    VerifyTablesAndColumns(db.db_.get());
-    VerifyDatabaseEmpty(db.db_.get());
+    VerifyTablesAndColumns(db.db_for_testing());
+    VerifyDatabaseEmpty(db.db_for_testing());
   }
 }
 
@@ -196,8 +196,8 @@
       ASSERT_TRUE(db.Init(file_name_));
       EXPECT_TRUE(expecter.SawExpectedErrors());
     }
-    VerifyTablesAndColumns(db.db_.get());
-    VerifyDatabaseEmpty(db.db_.get());
+    VerifyTablesAndColumns(db.db_for_testing());
+    VerifyDatabaseEmpty(db.db_for_testing());
   }
 }
 
@@ -272,8 +272,9 @@
       // Accessing the index will throw SQLITE_CORRUPT. The corruption handler
       // will recover the database and poison the handle, so the outer call
       // fails.
-      EXPECT_EQ(TopSitesDatabase::kRankOfNonExistingURL,
-                db.GetURLRank(MostVisitedURL(kUrl1, std::u16string())));
+      EXPECT_EQ(
+          TopSitesDatabase::kRankOfNonExistingURL,
+          db.GetURLRankForTesting(MostVisitedURL(kUrl1, std::u16string())));
 
       EXPECT_TRUE(expecter.SawExpectedErrors());
     }
@@ -291,11 +292,14 @@
   {
     TopSitesDatabase db;
     ASSERT_TRUE(db.Init(file_name_));
-    VerifyTablesAndColumns(db.db_.get());
+    VerifyTablesAndColumns(db.db_for_testing());
 
-    EXPECT_EQ(0, db.GetURLRank(MostVisitedURL(kUrl0, std::u16string())));
-    EXPECT_EQ(1, db.GetURLRank(MostVisitedURL(kUrl1, std::u16string())));
-    EXPECT_EQ(2, db.GetURLRank(MostVisitedURL(kUrl2, std::u16string())));
+    EXPECT_EQ(0,
+              db.GetURLRankForTesting(MostVisitedURL(kUrl0, std::u16string())));
+    EXPECT_EQ(1,
+              db.GetURLRankForTesting(MostVisitedURL(kUrl1, std::u16string())));
+    EXPECT_EQ(2,
+              db.GetURLRankForTesting(MostVisitedURL(kUrl2, std::u16string())));
 
     MostVisitedURLList urls;
     db.GetSites(&urls);
@@ -342,8 +346,9 @@
       // Accessing the index will throw SQLITE_CORRUPT. The corruption handler
       // will recover the database and poison the handle, so the outer call
       // fails.
-      EXPECT_EQ(TopSitesDatabase::kRankOfNonExistingURL,
-                db.GetURLRank(MostVisitedURL(kUrl0, std::u16string())));
+      EXPECT_EQ(
+          TopSitesDatabase::kRankOfNonExistingURL,
+          db.GetURLRankForTesting(MostVisitedURL(kUrl0, std::u16string())));
 
       EXPECT_TRUE(expecter.SawExpectedErrors());
     }
@@ -361,12 +366,14 @@
   {
     TopSitesDatabase db;
     ASSERT_TRUE(db.Init(file_name_));
-    VerifyTablesAndColumns(db.db_.get());
+    VerifyTablesAndColumns(db.db_for_testing());
 
-    EXPECT_EQ(0, db.GetURLRank(MostVisitedURL(kUrl0, std::u16string())));
-    EXPECT_EQ(1, db.GetURLRank(MostVisitedURL(kUrl2, std::u16string())));
+    EXPECT_EQ(0,
+              db.GetURLRankForTesting(MostVisitedURL(kUrl0, std::u16string())));
+    EXPECT_EQ(1,
+              db.GetURLRankForTesting(MostVisitedURL(kUrl2, std::u16string())));
     EXPECT_EQ(TopSitesDatabase::kRankOfNonExistingURL,
-              db.GetURLRank(MostVisitedURL(kUrl1, std::u16string())));
+              db.GetURLRankForTesting(MostVisitedURL(kUrl1, std::u16string())));
 
     MostVisitedURLList urls;
     db.GetSites(&urls);
diff --git a/components/media_router/browser/issue_manager.cc b/components/media_router/browser/issue_manager.cc
index 05c61774..c5f49a4 100644
--- a/components/media_router/browser/issue_manager.cc
+++ b/components/media_router/browser/issue_manager.cc
@@ -29,8 +29,7 @@
 // static
 base::TimeDelta IssueManager::GetAutoDismissTimeout(
     const IssueInfo& issue_info) {
-  if (issue_info.is_blocking)
-    return base::TimeDelta();
+  DCHECK(!issue_info.is_blocking);
 
   switch (issue_info.severity) {
     case IssueInfo::Severity::NOTIFICATION:
@@ -54,9 +53,7 @@
 
 void IssueManager::AddIssue(const IssueInfo& issue_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto& issues_map =
-      issue_info.is_blocking ? blocking_issues_ : non_blocking_issues_;
-  for (const auto& key_value_pair : issues_map) {
+  for (const auto& key_value_pair : issues_map_) {
     const auto& issue = key_value_pair.second->issue;
     if (issue.info() == issue_info)
       return;
@@ -73,23 +70,23 @@
                                   timeout);
   }
 
-  issues_map.emplace(issue.id(), std::make_unique<IssueManager::Entry>(
-                                     issue, std::move(cancelable_dismiss_cb)));
+  issues_map_.emplace(issue.id(), std::make_unique<IssueManager::Entry>(
+                                      issue, std::move(cancelable_dismiss_cb)));
   MaybeUpdateTopIssue();
 }
 
 void IssueManager::ClearIssue(const Issue::Id& issue_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (non_blocking_issues_.erase(issue_id) || blocking_issues_.erase(issue_id))
+  if (issues_map_.erase(issue_id))
     MaybeUpdateTopIssue();
 }
 
-void IssueManager::ClearNonBlockingIssues() {
+void IssueManager::ClearAllIssues() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (non_blocking_issues_.empty())
+  if (issues_map_.empty())
     return;
 
-  non_blocking_issues_.clear();
+  issues_map_.clear();
   MaybeUpdateTopIssue();
 }
 
@@ -126,12 +123,9 @@
 
 void IssueManager::MaybeUpdateTopIssue() {
   const Issue* new_top_issue = nullptr;
-  // Select the first blocking issue in the list of issues.
-  // If there are none, simply select the first issue in the list.
-  if (!blocking_issues_.empty()) {
-    new_top_issue = &blocking_issues_.begin()->second->issue;
-  } else if (!non_blocking_issues_.empty()) {
-    new_top_issue = &non_blocking_issues_.begin()->second->issue;
+  // Select the first issue in the list of issues.
+  if (!issues_map_.empty()) {
+    new_top_issue = &issues_map_.begin()->second->issue;
   }
 
   // If we've found a new top issue, then report it via the observer.
diff --git a/components/media_router/browser/issue_manager.h b/components/media_router/browser/issue_manager.h
index 4ea5f86..0debbf1 100644
--- a/components/media_router/browser/issue_manager.h
+++ b/components/media_router/browser/issue_manager.h
@@ -7,11 +7,10 @@
 
 #include <stddef.h>
 
-#include <map>
 #include <memory>
 
 #include "base/cancelable_callback.h"
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
@@ -46,8 +45,8 @@
   // |issue_id|: Issue::Id of the issue to be removed.
   void ClearIssue(const Issue::Id& issue_id);
 
-  // Clears all non-blocking issues.
-  void ClearNonBlockingIssues();
+  // Clears all issues.
+  void ClearAllIssues();
 
   // Clears the top issue if it belongs to the given sink_id.
   void ClearTopIssueForSink(const MediaSink::Id& sink_id);
@@ -95,11 +94,7 @@
   // notified of the new top issue.
   void MaybeUpdateTopIssue();
 
-  // TODO(crbug.com/1352864): Blocking issues are no longer used and can be
-  // removed.
-  base::small_map<std::map<Issue::Id, std::unique_ptr<Entry>>> blocking_issues_;
-  base::small_map<std::map<Issue::Id, std::unique_ptr<Entry>>>
-      non_blocking_issues_;
+  base::flat_map<Issue::Id, std::unique_ptr<Entry>> issues_map_;
 
   // IssueObserver instances are not owned by the manager.
   base::ObserverList<IssuesObserver>::Unchecked issues_observers_;
@@ -109,7 +104,7 @@
 
   // The SingleThreadTaskRunner that this IssueManager runs on, and is used
   // for posting issue auto-dismissal tasks.
-  // When a non-blocking issues is added to the IssueManager, a delayed task
+  // When an issue is added to the IssueManager, a delayed task
   // will be added to remove the issue. This is done to automatically clean up
   // issues that are no longer relevant.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/components/media_router/browser/issue_manager_unittest.cc b/components/media_router/browser/issue_manager_unittest.cc
index 33eda596..1d672bc 100644
--- a/components/media_router/browser/issue_manager_unittest.cc
+++ b/components/media_router/browser/issue_manager_unittest.cc
@@ -51,31 +51,51 @@
   observer.Init();
   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
   EXPECT_EQ(issue_info1, issue1.info());
-  Issue::Id issue1_id = issue1.id();
   EXPECT_FALSE(issue1.info().is_blocking);
 
-  IssueInfo issue_info2 = CreateTestIssue(IssueInfo::Severity::FATAL);
-  EXPECT_TRUE(issue_info2.is_blocking);
+  IssueInfo issue_info2 = CreateTestIssue(IssueInfo::Severity::NOTIFICATION);
+  EXPECT_FALSE(issue_info2.is_blocking);
 
-  // Blocking issue takes precedence.
   Issue issue2((IssueInfo()));
-  EXPECT_CALL(observer, OnIssue(_)).WillOnce(SaveArg<0>(&issue2));
   manager_.AddIssue(issue_info2);
+
+  // Clear |issue1|. Observer will be notified with |issue2|.
+  EXPECT_CALL(observer, OnIssue(_)).WillOnce(SaveArg<0>(&issue2));
+  manager_.ClearIssue(issue1.id());
   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
   EXPECT_EQ(issue_info2, issue2.info());
 
-  // Clear |issue2|. Observer will be notified with |issue1| again as it is now
-  // the top issue.
-  EXPECT_CALL(observer, OnIssue(_)).WillOnce(SaveArg<0>(&issue1));
-  manager_.ClearIssue(issue2.id());
-  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
-  EXPECT_EQ(issue1_id, issue1.id());
-  EXPECT_EQ(issue_info1, issue1.info());
-
   // All issues cleared. Observer will be notified with |nullptr| that there are
   // no more issues.
   EXPECT_CALL(observer, OnIssuesCleared());
+  manager_.ClearIssue(issue2.id());
+}
+
+TEST_F(IssueManagerTest, IssuesDeletionShouldNotAffectOrder) {
+  MockIssuesObserver observer(&manager_);
+  observer.Init();
+
+  Issue issue1((IssueInfo()));
+  EXPECT_CALL(observer, OnIssue(_)).WillOnce(SaveArg<0>(&issue1));
+
+  IssueInfo issue_info1 = CreateTestIssue(IssueInfo::Severity::NOTIFICATION);
+  manager_.AddIssue(issue_info1);
+
+  IssueInfo issue_info2 = CreateTestIssue(IssueInfo::Severity::WARNING);
+  manager_.AddIssue(issue_info2);
+
+  IssueInfo issue_info3 = CreateTestIssue(IssueInfo::Severity::NOTIFICATION);
+  manager_.AddIssue(issue_info3);
+
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
+  EXPECT_EQ(issue_info1, issue1.info());
+
+  Issue issue2((IssueInfo()));
+  EXPECT_CALL(observer, OnIssue(_)).WillOnce(SaveArg<0>(&issue2));
   manager_.ClearIssue(issue1.id());
+  EXPECT_EQ(issue_info2, issue2.info());
+
+  manager_.ClearAllIssues();
 }
 
 TEST_F(IssueManagerTest, AddSameIssueInfoHasNoEffect) {
@@ -97,7 +117,7 @@
   manager_.ClearIssue(issue.id());
 }
 
-TEST_F(IssueManagerTest, NonBlockingIssuesGetAutoDismissed) {
+TEST_F(IssueManagerTest, IssuesGetAutoDismissed) {
   MockIssuesObserver observer(&manager_);
   observer.Init();
 
@@ -143,31 +163,7 @@
   EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
 }
 
-TEST_F(IssueManagerTest, BlockingIssuesDoNotGetAutoDismissed) {
-  MockIssuesObserver observer(&manager_);
-  observer.Init();
-
-  EXPECT_CALL(observer, OnIssue(_)).Times(1);
-  IssueInfo issue_info1 = CreateTestIssue(IssueInfo::Severity::WARNING);
-  issue_info1.is_blocking = true;
-  manager_.AddIssue(issue_info1);
-
-  EXPECT_CALL(observer, OnIssuesCleared()).Times(0);
-
-  base::TimeDelta timeout = IssueManager::GetAutoDismissTimeout(issue_info1);
-  EXPECT_TRUE(timeout.is_zero());
-  EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
-
-  // FATAL issues are always blocking.
-  IssueInfo issue_info2 = CreateTestIssue(IssueInfo::Severity::FATAL);
-  manager_.AddIssue(issue_info2);
-
-  timeout = IssueManager::GetAutoDismissTimeout(issue_info2);
-  EXPECT_TRUE(timeout.is_zero());
-  EXPECT_EQ(task_environment_.GetPendingMainThreadTaskCount(), 0u);
-}
-
-TEST_F(IssueManagerTest, ClearNonBlockingIssues) {
+TEST_F(IssueManagerTest, ClearAllIssues) {
   MockIssuesObserver observer(&manager_);
   observer.Init();
 
@@ -176,37 +172,7 @@
   manager_.AddIssue(CreateTestIssue(IssueInfo::Severity::WARNING));
 
   EXPECT_CALL(observer, OnIssuesCleared()).Times(1);
-  manager_.ClearNonBlockingIssues();
-}
-
-TEST_F(IssueManagerTest, ClearNonBlockingIssuesDoesNotClearBlockingIssue) {
-  MockIssuesObserver observer(&manager_);
-  observer.Init();
-
-  // Add a blocking issue and a couple of non-blocking issues.
-  Issue blocking_issue((IssueInfo()));
-  EXPECT_CALL(observer, OnIssue(_))
-      .Times(1)
-      .WillOnce(SaveArg<0>(&blocking_issue));
-  manager_.AddIssue(CreateTestIssue(IssueInfo::Severity::FATAL));
-  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer));
-
-  manager_.AddIssue(CreateTestIssue(IssueInfo::Severity::NOTIFICATION));
-  manager_.AddIssue(CreateTestIssue(IssueInfo::Severity::WARNING));
-
-  EXPECT_CALL(observer, OnIssuesCleared()).Times(0);
-
-  // The blocking issue remains.
-  manager_.ClearNonBlockingIssues();
-
-  Issue same_blocking_issue((IssueInfo()));
-  MockIssuesObserver observer2(&manager_);
-  EXPECT_CALL(observer2, OnIssue(_))
-      .Times(1)
-      .WillOnce(SaveArg<0>(&same_blocking_issue));
-  observer2.Init();
-  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&observer2));
-  EXPECT_EQ(blocking_issue.id(), same_blocking_issue.id());
+  manager_.ClearAllIssues();
 }
 
 }  // namespace media_router
diff --git a/components/mirroring/service/media_remoter.cc b/components/mirroring/service/media_remoter.cc
index d82bdd2..c52f6a5b 100644
--- a/components/mirroring/service/media_remoter.cc
+++ b/components/mirroring/service/media_remoter.cc
@@ -46,36 +46,39 @@
     scoped_refptr<media::cast::CastEnvironment> cast_environment,
     openscreen::cast::Sender* audio_sender,
     openscreen::cast::Sender* video_sender,
-    const FrameSenderConfig& audio_config,
-    const FrameSenderConfig& video_config) {
+    absl::optional<FrameSenderConfig> audio_config,
+    absl::optional<FrameSenderConfig> video_config) {
   DCHECK(audio_sender || video_sender);
   DCHECK(!openscreen_audio_sender_);
   DCHECK(!openscreen_video_sender_);
   DCHECK(!transport_);
+  DCHECK(base::FeatureList::IsEnabled(media::kOpenscreenCastStreamingSession));
 
   openscreen_audio_sender_ = audio_sender;
   openscreen_video_sender_ = video_sender;
-  StartRpcMessagingInternal(std::move(cast_environment), audio_config,
-                            video_config);
+  StartRpcMessagingInternal(std::move(cast_environment),
+                            std::move(audio_config), std::move(video_config));
 }
 
 void MediaRemoter::StartRpcMessaging(
     scoped_refptr<media::cast::CastEnvironment> cast_environment,
     media::cast::CastTransport* transport,
-    const FrameSenderConfig& audio_config,
-    const FrameSenderConfig& video_config) {
+    absl::optional<FrameSenderConfig> audio_config,
+    absl::optional<FrameSenderConfig> video_config) {
   DCHECK(!openscreen_audio_sender_);
   DCHECK(!openscreen_video_sender_);
   DCHECK(!transport_);
+  DCHECK(!base::FeatureList::IsEnabled(media::kOpenscreenCastStreamingSession));
 
   transport_ = transport;
-  StartRpcMessagingInternal(cast_environment, audio_config, video_config);
+  StartRpcMessagingInternal(std::move(cast_environment),
+                            std::move(audio_config), std::move(video_config));
 }
 
 void MediaRemoter::StartRpcMessagingInternal(
     scoped_refptr<media::cast::CastEnvironment> cast_environment,
-    const FrameSenderConfig& audio_config,
-    const FrameSenderConfig& video_config) {
+    absl::optional<FrameSenderConfig> audio_config,
+    absl::optional<FrameSenderConfig> video_config) {
   DCHECK(!cast_environment_);
 
   if (state_ != STARTING_REMOTING) {
@@ -88,8 +91,8 @@
   // A remoting streaming session started. Start RPC message transport and
   // notify the remoting source to start data streaming.
   cast_environment_ = std::move(cast_environment);
-  audio_config_ = audio_config;
-  video_config_ = video_config;
+  audio_config_ = std::move(audio_config);
+  video_config_ = std::move(video_config);
   rpc_dispatcher_->Subscribe(base::BindRepeating(
       &MediaRemoter::OnMessageFromSink, weak_factory_.GetWeakPtr()));
   state_ = REMOTING_STARTED;
@@ -118,19 +121,21 @@
 }
 
 void MediaRemoter::Stop(media::mojom::RemotingStopReason reason) {
-  if (state_ != STARTING_REMOTING && state_ != REMOTING_STARTED)
+  if (state_ == STOPPING_REMOTING || state_ == MIRRORING) {
     return;
-  if (state_ == REMOTING_STARTED) {
-    rpc_dispatcher_->Unsubscribe();
-    audio_sender_.reset();
-    video_sender_.reset();
-    cast_environment_ = nullptr;
-    transport_ = nullptr;
-    openscreen_audio_sender_ = nullptr;
-    openscreen_video_sender_ = nullptr;
-    audio_config_ = FrameSenderConfig();
-    video_config_ = FrameSenderConfig();
   }
+
+  // At this point, we are currently remoting and should tear down.
+  rpc_dispatcher_->Unsubscribe();
+  audio_sender_.reset();
+  video_sender_.reset();
+  cast_environment_ = nullptr;
+  transport_ = nullptr;
+  openscreen_audio_sender_ = nullptr;
+  openscreen_video_sender_ = nullptr;
+  audio_config_ = absl::nullopt;
+  video_config_ = absl::nullopt;
+
   state_ = STOPPING_REMOTING;
   remoting_source_->OnStopped(reason);
   // Prevent the start of remoting until switching completes.
@@ -178,23 +183,23 @@
         video_sender_receiver) {
   DCHECK(openscreen_audio_sender_ || openscreen_video_sender_);
 
-  if (audio_pipe.is_valid() &&
-      audio_config_.codec == Codec::CODEC_AUDIO_REMOTE &&
+  if (audio_pipe.is_valid() && audio_config_ &&
+      audio_config_->codec == Codec::CODEC_AUDIO_REMOTE &&
       openscreen_audio_sender_) {
     // NOTE: use of base::Unretained is safe because we own the sender.
     audio_sender_ = std::make_unique<RemotingSender>(
-        cast_environment_, openscreen_audio_sender_, audio_config_,
+        cast_environment_, openscreen_audio_sender_, *audio_config_,
         std::move(audio_pipe), std::move(audio_sender_receiver),
         base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
                        base::Unretained(this)));
   }
 
-  if (video_pipe.is_valid() &&
-      video_config_.codec == Codec::CODEC_VIDEO_REMOTE &&
+  if (video_pipe.is_valid() && video_config_ &&
+      video_config_->codec == Codec::CODEC_VIDEO_REMOTE &&
       openscreen_video_sender_) {
     // NOTE: use of base::Unretained is safe because we own the sender.
     video_sender_ = std::make_unique<RemotingSender>(
-        cast_environment_, openscreen_video_sender_, video_config_,
+        cast_environment_, openscreen_video_sender_, *video_config_,
         std::move(video_pipe), std::move(video_sender_receiver),
         base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
                        base::Unretained(this)));
@@ -208,21 +213,21 @@
         audio_sender_receiver,
     mojo::PendingReceiver<media::mojom::RemotingDataStreamSender>
         video_sender_receiver) {
-  if (audio_pipe.is_valid() &&
-      audio_config_.codec == Codec::CODEC_AUDIO_REMOTE) {
+  if (audio_pipe.is_valid() && audio_config_ &&
+      audio_config_->codec == Codec::CODEC_AUDIO_REMOTE) {
     DCHECK(transport_);
     audio_sender_ = std::make_unique<RemotingSender>(
-        cast_environment_, transport_, audio_config_, std::move(audio_pipe),
+        cast_environment_, transport_, *audio_config_, std::move(audio_pipe),
         std::move(audio_sender_receiver),
         base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
                        base::Unretained(this)));
   }
 
-  if (video_pipe.is_valid() &&
-      video_config_.codec == Codec::CODEC_VIDEO_REMOTE) {
+  if (video_pipe.is_valid() && video_config_ &&
+      video_config_->codec == Codec::CODEC_VIDEO_REMOTE) {
     DCHECK(transport_);
     video_sender_ = std::make_unique<RemotingSender>(
-        cast_environment_, transport_, video_config_, std::move(video_pipe),
+        cast_environment_, transport_, *video_config_, std::move(video_pipe),
         std::move(video_sender_receiver),
         base::BindOnce(&MediaRemoter::OnRemotingDataStreamError,
                        base::Unretained(this)));
@@ -244,8 +249,8 @@
 void MediaRemoter::OnRemotingDataStreamError() {
   if (state_ != REMOTING_STARTED)
     return;
-  state_ = REMOTING_DISABLED;
   Stop(media::mojom::RemotingStopReason::DATA_SEND_FAILED);
+  state_ = REMOTING_DISABLED;
 }
 
 }  // namespace mirroring
diff --git a/components/mirroring/service/media_remoter.h b/components/mirroring/service/media_remoter.h
index 0f32712..db32a5b 100644
--- a/components/mirroring/service/media_remoter.h
+++ b/components/mirroring/service/media_remoter.h
@@ -13,6 +13,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace openscreen::cast {
 class Sender;
@@ -81,23 +82,19 @@
   // and must either outlive `this` or live until `Stop` is called. If
   // either is nullptr, the associated config should be default constructed as
   // it is ignored.
-  // TODO(https://crbug.com/1363516): make audio and video configs
-  // absl::optional instead of default constructed.
   void StartRpcMessaging(
       scoped_refptr<media::cast::CastEnvironment> cast_environment,
       openscreen::cast::Sender* audio_sender,
       openscreen::cast::Sender* video_sender,
-      const media::cast::FrameSenderConfig& audio_config,
-      const media::cast::FrameSenderConfig& video_config);
+      absl::optional<media::cast::FrameSenderConfig> audio_config,
+      absl::optional<media::cast::FrameSenderConfig> video_config);
 
   // Old way using a cast transport.
-  // TODO(https://crbug.com/1316434): should be removed once libcast sender is
-  // successfully launched.
   void StartRpcMessaging(
       scoped_refptr<media::cast::CastEnvironment> cast_environment,
       media::cast::CastTransport* transport,
-      const media::cast::FrameSenderConfig& audio_config,
-      const media::cast::FrameSenderConfig& video_config);
+      absl::optional<media::cast::FrameSenderConfig> audio_config,
+      absl::optional<media::cast::FrameSenderConfig> video_config);
 
   // Called when a mirroring session is successfully resumed.
   void OnMirroringResumed();
@@ -145,8 +142,8 @@
   // Called by the public |StartRpcMessaging| methods.
   void StartRpcMessagingInternal(
       scoped_refptr<media::cast::CastEnvironment> cast_environment,
-      const media::cast::FrameSenderConfig& audio_config,
-      const media::cast::FrameSenderConfig& video_config);
+      absl::optional<media::cast::FrameSenderConfig> audio_config,
+      absl::optional<media::cast::FrameSenderConfig> video_config);
 
   // Called by RemotingSender when error occurred. Will stop this remoting
   // session and fallback to mirroring.
@@ -169,8 +166,8 @@
   raw_ptr<openscreen::cast::Sender> openscreen_audio_sender_ = nullptr;
   raw_ptr<openscreen::cast::Sender> openscreen_video_sender_ = nullptr;
 
-  media::cast::FrameSenderConfig audio_config_;
-  media::cast::FrameSenderConfig video_config_;
+  absl::optional<media::cast::FrameSenderConfig> audio_config_;
+  absl::optional<media::cast::FrameSenderConfig> video_config_;
 
   // State transition diagram:
   //
diff --git a/components/mirroring/service/media_remoter_unittest.cc b/components/mirroring/service/media_remoter_unittest.cc
index c4810a6..d75e2d28 100644
--- a/components/mirroring/service/media_remoter_unittest.cc
+++ b/components/mirroring/service/media_remoter_unittest.cc
@@ -6,13 +6,17 @@
 
 #include "base/run_loop.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/default_tick_clock.h"
+#include "components/mirroring/mojom/session_parameters.mojom.h"
 #include "components/mirroring/service/message_dispatcher.h"
 #include "components/mirroring/service/mirror_settings.h"
 #include "components/mirroring/service/rpc_dispatcher_impl.h"
 #include "components/openscreen_platform/task_runner.h"
+#include "media/base/media_switches.h"
 #include "media/cast/cast_environment.h"
+#include "media/cast/test/mock_cast_transport.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -23,13 +27,14 @@
 #include "third_party/openscreen/src/platform/api/time.h"
 #include "third_party/openscreen/src/platform/base/trivial_clock_traits.h"
 
-using ::testing::InvokeWithoutArgs;
-using ::testing::_;
-using ::testing::Mock;
+using media::cast::Codec;
+using media::cast::RtpPayloadType;
 using media::mojom::RemotingSinkMetadata;
 using media::mojom::RemotingStopReason;
-using media::cast::RtpPayloadType;
-using media::cast::Codec;
+using mirroring::mojom::SessionType;
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Mock;
 
 namespace mirroring {
 
@@ -80,6 +85,17 @@
   openscreen::cast::Sender video_sender;
 };
 
+// Mojo handles used for managing the remoting data streams.
+struct DataStreamHandles {
+  mojo::ScopedDataPipeProducerHandle audio_producer_handle;
+  mojo::ScopedDataPipeProducerHandle video_producer_handle;
+
+  mojo::PendingRemote<media::mojom::RemotingDataStreamSender>
+      audio_stream_sender;
+  mojo::PendingRemote<media::mojom::RemotingDataStreamSender>
+      video_stream_sender;
+};
+
 class MockRemotingSource : public media::mojom::RemotingSource {
  public:
   MockRemotingSource() = default;
@@ -179,6 +195,11 @@
     Mock::VerifyAndClear(&remoting_source_);
   }
 
+  // Should only be called once per test.
+  void EnableOpenscreenCastStreamingSession() {
+    feature_list_.InitAndEnableFeature(media::kOpenscreenCastStreamingSession);
+  }
+
   // Signals that a remoting streaming session starts successfully.
   void RemotingStreamingStarted(bool should_use_openscreen_senders) {
     ASSERT_TRUE(media_remoter_);
@@ -200,7 +221,7 @@
                                                 Codec::CODEC_VIDEO_REMOTE));
     } else {
       media_remoter_->StartRpcMessaging(
-          cast_environment, nullptr, media::cast::FrameSenderConfig(),
+          cast_environment, &mock_transport_, media::cast::FrameSenderConfig(),
           MirrorSettings::GetDefaultVideoConfig(RtpPayloadType::REMOTE_VIDEO,
                                                 Codec::CODEC_VIDEO_REMOTE));
     }
@@ -228,12 +249,45 @@
     Mock::VerifyAndClear(&remoting_source_);
   }
 
+  void StartDataStreams(SessionType session_type) {
+    static constexpr int kDataPipeCapacity = 1000;
+    data_stream_handles_ = std::make_unique<DataStreamHandles>();
+    mojo::ScopedDataPipeConsumerHandle audio_consumer_handle;
+    mojo::ScopedDataPipeConsumerHandle video_consumer_handle;
+
+    if (session_type != SessionType::VIDEO_ONLY) {
+      EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(
+                                    kDataPipeCapacity,
+                                    data_stream_handles_->audio_producer_handle,
+                                    audio_consumer_handle));
+    }
+
+    if (session_type != SessionType::AUDIO_ONLY) {
+      EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(
+                                    kDataPipeCapacity,
+                                    data_stream_handles_->video_producer_handle,
+                                    video_consumer_handle));
+    }
+
+    remoter_->StartDataStreams(std::move(audio_consumer_handle),
+                               std::move(video_consumer_handle),
+                               (session_type != SessionType::VIDEO_ONLY)
+                                   ? data_stream_handles_->audio_stream_sender
+                                         .InitWithNewPipeAndPassReceiver()
+                                   : mojo::NullReceiver(),
+                               (session_type != SessionType::AUDIO_ONLY)
+                                   ? data_stream_handles_->video_stream_sender
+                                         .InitWithNewPipeAndPassReceiver()
+                                   : mojo::NullReceiver());
+  }
+
   testing::StrictMock<MockRemotingSource>& remoting_source() {
     return remoting_source_;
   }
 
  private:
   base::test::TaskEnvironment task_environment_;
+  base::test::ScopedFeatureList feature_list_;
   mojo::Receiver<mojom::CastMessageChannel> receiver_{this};
   base::MockCallback<MessageDispatcher::ErrorCallback> error_callback_;
   mojo::Remote<mojom::CastMessageChannel> inbound_channel_;
@@ -242,21 +296,54 @@
   const media::mojom::RemotingSinkMetadata sink_metadata_;
   testing::StrictMock<MockRemotingSource> remoting_source_;
   mojo::Remote<media::mojom::Remoter> remoter_;
+  testing::NiceMock<media::cast::MockCastTransport> mock_transport_;
 
   // Configured for use by the media remoter.
   std::unique_ptr<OpenscreenTestSenders> openscreen_test_senders_;
+  std::unique_ptr<DataStreamHandles> data_stream_handles_;
   std::unique_ptr<MediaRemoter> media_remoter_;
 };
 
 TEST_P(MediaRemoterTest, StartAndStopRemoting) {
+  if (GetParam()) {
+    EnableOpenscreenCastStreamingSession();
+  }
   CreateRemoter();
   StartRemoting();
   EXPECT_CALL(remoting_source(), OnStarted());
   RemotingStreamingStarted(GetParam());
+  StartDataStreams(SessionType::AUDIO_AND_VIDEO);
+  StopRemoting();
+}
+
+TEST_P(MediaRemoterTest, StartAndStopRemotingAudioOnly) {
+  if (GetParam()) {
+    EnableOpenscreenCastStreamingSession();
+  }
+  CreateRemoter();
+  StartRemoting();
+  EXPECT_CALL(remoting_source(), OnStarted());
+  RemotingStreamingStarted(GetParam());
+  StartDataStreams(SessionType::AUDIO_ONLY);
+  StopRemoting();
+}
+
+TEST_P(MediaRemoterTest, StartAndStopRemotingVideoOnly) {
+  if (GetParam()) {
+    EnableOpenscreenCastStreamingSession();
+  }
+  CreateRemoter();
+  StartRemoting();
+  EXPECT_CALL(remoting_source(), OnStarted());
+  RemotingStreamingStarted(GetParam());
+  StartDataStreams(SessionType::VIDEO_ONLY);
   StopRemoting();
 }
 
 TEST_P(MediaRemoterTest, StartRemotingWithoutCallingStart) {
+  if (GetParam()) {
+    EnableOpenscreenCastStreamingSession();
+  }
   CreateRemoter();
   // Should fail since we didn't call `StartRemoting().`
   EXPECT_CALL(remoting_source(), OnStarted()).Times(0);
@@ -283,12 +370,16 @@
 }
 
 TEST_P(MediaRemoterTest, SwitchBetweenMultipleSessions) {
+  if (GetParam()) {
+    EnableOpenscreenCastStreamingSession();
+  }
   CreateRemoter();
 
   // Start a remoting session.
   StartRemoting();
   EXPECT_CALL(remoting_source(), OnStarted());
   RemotingStreamingStarted(GetParam());
+  StartDataStreams(SessionType::AUDIO_AND_VIDEO);
 
   // Stop the remoting session and switch to mirroring.
   StopRemoting();
@@ -298,6 +389,7 @@
   StartRemoting();
   EXPECT_CALL(remoting_source(), OnStarted());
   RemotingStreamingStarted(GetParam());
+  StartDataStreams(SessionType::AUDIO_AND_VIDEO);
 
   // Switch to mirroring again.
   StopRemoting();
diff --git a/components/mirroring/service/openscreen_session_host.cc b/components/mirroring/service/openscreen_session_host.cc
index e6200f87..7342712 100644
--- a/components/mirroring/service/openscreen_session_host.cc
+++ b/components/mirroring/service/openscreen_session_host.cc
@@ -19,6 +19,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -72,6 +73,8 @@
 // The time between updating the bandwidth estimates.
 constexpr base::TimeDelta kBandwidthUpdateInterval = base::Milliseconds(500);
 
+constexpr char kLogPrefix[] = "OpenscreenSessionHost: ";
+
 int NumberOfEncodeThreads() {
   // Do not saturate CPU utilization just for encoding. On a lower-end system
   // with only 1 or 2 cores, use only one thread for encoding. On systems with
@@ -193,12 +196,12 @@
   if (session_params.target_playout_delay) {
     // TODO(https://crbug.com/1363694): animated playout delay should be
     // removed.
-    config.animated_playout_delay = session_params.target_playout_delay.value();
+    config.animated_playout_delay = *session_params.target_playout_delay;
 
     // TODO(https://crbug.com/1363017): adaptive playout delay should be
     // re-enabled.
-    config.min_playout_delay = session_params.target_playout_delay.value();
-    config.max_playout_delay = session_params.target_playout_delay.value();
+    config.min_playout_delay = *session_params.target_playout_delay;
+    config.max_playout_delay = *session_params.target_playout_delay;
   }
 }
 
@@ -369,10 +372,6 @@
     const openscreen::cast::SenderSession* session,
     openscreen::cast::SenderSession::ConfiguredSenders senders,
     Recommendations capture_recommendations) {
-  DVLOG(1) << __func__ << ": negotiated a new "
-           << (state_ == State::kRemoting ? "remoting" : "mirroring")
-           << " session.";
-
   if (state_ == State::kStopped)
     return;
 
@@ -434,8 +433,7 @@
 
     media_remoter_->StartRpcMessaging(
         cast_environment_, senders.audio_sender, senders.video_sender,
-        audio_config.value_or(media::cast::FrameSenderConfig{}),
-        video_config.value_or(media::cast::FrameSenderConfig{}));
+        std::move(audio_config), std::move(video_config));
 
     return;
   }
@@ -443,7 +441,7 @@
   SetConstraints(capture_recommendations, audio_config, video_config);
   if (senders.audio_sender) {
     auto audio_sender = std::make_unique<media::cast::AudioSender>(
-        cast_environment_, audio_config.value(),
+        cast_environment_, *audio_config,
         base::BindOnce(&OpenscreenSessionHost::OnEncoderStatusChange,
                        // Safe because we own `audio_stream`.
                        weak_factory_.GetWeakPtr()),
@@ -472,7 +470,7 @@
 
   if (senders.video_sender) {
     auto video_sender = std::make_unique<media::cast::VideoSender>(
-        cast_environment_, video_config.value(),
+        cast_environment_, *video_config,
         base::BindRepeating(&OpenscreenSessionHost::OnEncoderStatusChange,
                             weak_factory_.GetWeakPtr()),
         base::BindRepeating(
@@ -520,6 +518,22 @@
   if (initially_starting_session && observer_) {
     observer_->DidStart();
   }
+
+  LogInfoMessage(base::StringPrintf(
+      "negotiated a new %s session. audio codec=%s, video codec=%s (%s)",
+      (state_ == State::kRemoting ? "remoting" : "mirroring"),
+      // TODO(https://crbug.com/1363514): media::cast::Codec should be removed.
+      // For now, we log the integer value of the enum but this should be
+      // serialized to a string once we use AudioCodec, VideoCodec.
+      (audio_config
+           ? base::NumberToString(static_cast<int>(audio_config->codec)).c_str()
+           : "none"),
+      (video_config
+           ? base::NumberToString(static_cast<int>(video_config->codec)).c_str()
+           : "none"),
+      (video_config
+           ? (video_config->use_external_encoder ? "hardware" : "software")
+           : "n/a")));
 }
 
 void OpenscreenSessionHost::OnCapabilitiesDetermined(
@@ -573,7 +587,8 @@
 }
 
 void OpenscreenSessionHost::RequestRefreshFrame() {
-  DVLOG(3) << __func__;
+  LogInfoMessage("Requesting a refresh frame from the video client");
+
   if (video_capture_client_)
     video_capture_client_->RequestRefreshFrame();
 }
@@ -649,12 +664,19 @@
     std::move(initialized_cb_).Run();
 }
 
+void OpenscreenSessionHost::LogInfoMessage(const std::string& message) {
+  const std::string log_message = kLogPrefix + message;
+  DVLOG(1) << log_message;
+  if (observer_)
+    observer_->LogInfoMessage(log_message);
+}
+
 void OpenscreenSessionHost::ReportAndLogError(SessionError error,
                                               const std::string& message) {
   UMA_HISTOGRAM_ENUMERATION("MediaRouter.MirroringService.SessionError", error);
 
   if (observer_)
-    observer_->LogErrorMessage(message);
+    observer_->LogErrorMessage(kLogPrefix + message);
 
   if (state_ == State::kRemoting) {
     // Try to fallback to mirroring.
@@ -670,7 +692,9 @@
 }
 
 void OpenscreenSessionHost::StopStreaming() {
-  DVLOG(2) << __func__ << " state=" << static_cast<int>(state_);
+  LogInfoMessage(
+      base::StrCat({"stopped streaming. state=",
+                    base::NumberToString(static_cast<int>(state_))}));
   if (!cast_environment_)
     return;
 
@@ -688,7 +712,9 @@
 }
 
 void OpenscreenSessionHost::StopSession() {
-  DVLOG(1) << __func__ << " state=" << static_cast<int>(state_);
+  LogInfoMessage(
+      base::StrCat({"stopped session. state=",
+                    base::NumberToString(static_cast<int>(state_))}));
   if (state_ == State::kStopped)
     return;
 
@@ -817,6 +843,10 @@
     audio_stream_->SetTargetPlayoutDelay(playout_delay);
   if (video_stream_)
     video_stream_->SetTargetPlayoutDelay(playout_delay);
+
+  LogInfoMessage(base::StrCat(
+      {"Updated target playout delay to ",
+       base::NumberToString(playout_delay.InMilliseconds()), "ms"}));
 }
 
 void OpenscreenSessionHost::ProcessFeedback(
@@ -857,12 +887,11 @@
     bandwidth_being_utilized_ = usable_bandwidth;
   }
 
-  DVLOG(2) << ": updated bandwidth to " << bandwidth_being_utilized_ << "/"
-           << bandwidth_estimate_ << " ("
-           << static_cast<int>(
-                  (static_cast<float>(bandwidth_being_utilized_) * 100) /
-                  bandwidth_estimate_)
-           << "%)";
+  VLOG(2) << ": updated bandwidth to " << bandwidth_being_utilized_ << "/"
+          << bandwidth_estimate_ << " ("
+          << static_cast<int>(static_cast<float>(bandwidth_being_utilized_) *
+                              100 / bandwidth_estimate_)
+          << "%).";
 }
 
 void OpenscreenSessionHost::Negotiate() {
@@ -892,9 +921,9 @@
     last_offered_audio_config_ = MirrorSettings::GetDefaultAudioConfig(
         RtpPayloadType::AUDIO_OPUS, Codec::CODEC_AUDIO_OPUS);
     UpdateConfigUsingSessionParameters(session_params_,
-                                       last_offered_audio_config_.value());
+                                       *last_offered_audio_config_);
     audio_configs.push_back(
-        ToOpenscreenAudioConfig(last_offered_audio_config_.value()));
+        ToOpenscreenAudioConfig(*last_offered_audio_config_));
   }
 
   if (session_params_.type != SessionType::AUDIO_ONLY) {
diff --git a/components/mirroring/service/openscreen_session_host.h b/components/mirroring/service/openscreen_session_host.h
index 01c02f1..6da50fd4 100644
--- a/components/mirroring/service/openscreen_session_host.h
+++ b/components/mirroring/service/openscreen_session_host.h
@@ -141,6 +141,9 @@
   // to software rendering being used.
   void OnAsyncInitialized(const SupportedProfiles& profiles);
 
+  // Notify `observer_` of a relevant logging message.
+  void LogInfoMessage(const std::string& message);
+
   // Notify `observer_` that error occurred and close the session.
   void ReportAndLogError(mojom::SessionError error, const std::string& message);
 
diff --git a/components/mirroring/service/openscreen_session_host_unittest.cc b/components/mirroring/service/openscreen_session_host_unittest.cc
index f6336c3..1f1db13 100644
--- a/components/mirroring/service/openscreen_session_host_unittest.cc
+++ b/components/mirroring/service/openscreen_session_host_unittest.cc
@@ -236,7 +236,7 @@
     ASSERT_TRUE(last_sent_offer_);
 
     const openscreen::cast::Offer& offer =
-        absl::get<openscreen::cast::Offer>(last_sent_offer_.value().body);
+        absl::get<openscreen::cast::Offer>(last_sent_offer_->body);
     openscreen::cast::Answer answer{.udp_port = 1234};
 
     if (!offer.audio_streams.empty()) {
@@ -251,7 +251,7 @@
 
     openscreen::cast::ReceiverMessage receiver_message{
         .type = openscreen::cast::ReceiverMessage::Type::kAnswer,
-        .sequence_number = last_sent_offer_.value().sequence_number,
+        .sequence_number = last_sent_offer_->sequence_number,
         .valid = true,
         .body = std::move(answer)};
     Json::Value message_json = receiver_message.ToJson().value();
diff --git a/components/mirroring/service/rtp_stream.cc b/components/mirroring/service/rtp_stream.cc
index 97efc11..38d01ee2a 100644
--- a/components/mirroring/service/rtp_stream.cc
+++ b/components/mirroring/service/rtp_stream.cc
@@ -45,7 +45,7 @@
 void VideoRtpStream::InsertVideoFrame(
     scoped_refptr<media::VideoFrame> video_frame) {
   DCHECK(client_);
-  if (!video_frame->metadata().reference_time.has_value()) {
+  if (!video_frame->metadata().reference_time) {
     client_->OnError("Missing REFERENCE_TIME.");
     return;
   }
diff --git a/components/mirroring/service/session.cc b/components/mirroring/service/session.cc
index 7cfc8a39..0eb6bba78 100644
--- a/components/mirroring/service/session.cc
+++ b/components/mirroring/service/session.cc
@@ -151,9 +151,9 @@
   config.aes_iv_mask = aes_iv;
   config.sender_ssrc = sender_ssrc;
   if (session_params.target_playout_delay) {
-    config.animated_playout_delay = session_params.target_playout_delay.value();
-    config.min_playout_delay = session_params.target_playout_delay.value();
-    config.max_playout_delay = session_params.target_playout_delay.value();
+    config.animated_playout_delay = *session_params.target_playout_delay;
+    config.min_playout_delay = *session_params.target_playout_delay;
+    config.max_playout_delay = *session_params.target_playout_delay;
   }
   config_list->emplace_back(config);
 }
@@ -564,9 +564,10 @@
                                                      std::move(packet_events));
 }
 
-void Session::SetConstraints(const openscreen::cast::Answer& answer,
-                             FrameSenderConfig* audio_config,
-                             FrameSenderConfig* video_config) {
+void Session::ApplyConstraintsToConfigs(
+    const openscreen::cast::Answer& answer,
+    absl::optional<FrameSenderConfig>& audio_config,
+    absl::optional<FrameSenderConfig>& video_config) {
   const auto recommendations =
       openscreen::cast::capture_recommendations::GetRecommendations(answer);
   const auto& audio = recommendations.audio;
@@ -640,8 +641,8 @@
   // Select Audio/Video config from ANSWER.
   bool has_audio = false;
   bool has_video = false;
-  FrameSenderConfig audio_config;
-  FrameSenderConfig video_config;
+  absl::optional<FrameSenderConfig> audio_config;
+  absl::optional<FrameSenderConfig> video_config;
   const int video_start_idx = audio_configs.size();
   const int video_idx_bound = video_configs.size() + video_start_idx;
   for (size_t i = 0; i < answer.send_indexes.size(); ++i) {
@@ -657,7 +658,7 @@
         return;
       }
       audio_config = audio_configs[answer.send_indexes[i]];
-      audio_config.receiver_ssrc = answer.ssrcs[i];
+      audio_config->receiver_ssrc = answer.ssrcs[i];
       has_audio = true;
     } else {
       // Video
@@ -666,8 +667,8 @@
         return;
       }
       video_config = video_configs[answer.send_indexes[i] - video_start_idx];
-      video_config.receiver_ssrc = answer.ssrcs[i];
-      video_config.video_codec_params.number_of_encode_threads =
+      video_config->receiver_ssrc = answer.ssrcs[i];
+      video_config->video_codec_params.number_of_encode_threads =
           NumberOfEncodeThreads();
       has_video = true;
     }
@@ -678,8 +679,7 @@
   }
 
   // Set constraints from ANSWER message.
-  SetConstraints(answer, has_audio ? &audio_config : nullptr,
-                 has_video ? &video_config : nullptr);
+  ApplyConstraintsToConfigs(answer, audio_config, video_config);
 
   // Start streaming.
   const bool initially_starting_session =
@@ -710,14 +710,16 @@
 
   if (state_ == REMOTING) {
     DCHECK(media_remoter_);
-    DCHECK(audio_config.rtp_payload_type == RtpPayloadType::REMOTE_AUDIO ||
-           video_config.rtp_payload_type == RtpPayloadType::REMOTE_VIDEO);
+    DCHECK(!audio_config ||
+           audio_config->rtp_payload_type == RtpPayloadType::REMOTE_AUDIO);
+    DCHECK(!video_config ||
+           video_config->rtp_payload_type == RtpPayloadType::REMOTE_VIDEO);
     media_remoter_->StartRpcMessaging(cast_environment_, cast_transport_.get(),
                                       audio_config, video_config);
   } else /* MIRRORING */ {
     if (has_audio) {
       auto audio_sender = std::make_unique<media::cast::AudioSender>(
-          cast_environment_, audio_config,
+          cast_environment_, *audio_config,
           base::BindOnce(&Session::OnEncoderStatusChange,
                          weak_factory_.GetWeakPtr()),
           cast_transport_.get());
@@ -744,7 +746,7 @@
 
     if (has_video) {
       auto video_sender = std::make_unique<media::cast::VideoSender>(
-          cast_environment_, video_config,
+          cast_environment_, *video_config,
           base::BindRepeating(&Session::OnEncoderStatusChange,
                               weak_factory_.GetWeakPtr()),
           base::BindRepeating(&Session::CreateVideoEncodeAccelerator,
diff --git a/components/mirroring/service/session.h b/components/mirroring/service/session.h
index 82ab91c..0192e86 100644
--- a/components/mirroring/service/session.h
+++ b/components/mirroring/service/session.h
@@ -27,6 +27,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
 class AudioInputDevice;
@@ -83,10 +84,12 @@
       std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
       std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events);
 
-  // Helper method for setting constraints from the ANSWER response.
-  void SetConstraints(const openscreen::cast::Answer& answer,
-                      media::cast::FrameSenderConfig* audio_config,
-                      media::cast::FrameSenderConfig* video_config);
+  // Helper method for applying the constraints from |answer| to the audio and
+  // video configs.
+  void ApplyConstraintsToConfigs(
+      const openscreen::cast::Answer& answer,
+      absl::optional<media::cast::FrameSenderConfig>& audio_config,
+      absl::optional<media::cast::FrameSenderConfig>& video_config);
 
   // Callback for ANSWER response. If the ANSWER is invalid, |observer_| will
   // get notified with error, and session is stopped. Otherwise, capturing and
diff --git a/components/mirroring/service/video_capture_client.cc b/components/mirroring/service/video_capture_client.cc
index 573b254..38cc04b 100644
--- a/components/mirroring/service/video_capture_client.cc
+++ b/components/mirroring/service/video_capture_client.cc
@@ -275,8 +275,8 @@
   }
 
   frame->set_metadata(buffer->info->metadata);
-  if (buffer->info->color_space.has_value())
-    frame->set_color_space(buffer->info->color_space.value());
+  if (buffer->info->color_space)
+    frame->set_color_space(*buffer->info->color_space);
 
   frame_deliver_callback_.Run(frame);
 }
diff --git a/components/omnibox/browser/history_fuzzy_provider.cc b/components/omnibox/browser/history_fuzzy_provider.cc
index 7f9199e..a268b6eb 100644
--- a/components/omnibox/browser/history_fuzzy_provider.cc
+++ b/components/omnibox/browser/history_fuzzy_provider.cc
@@ -286,6 +286,8 @@
 bool Node::FindCorrections(const std::u16string& text,
                            ToleranceSchedule tolerance_schedule,
                            std::vector<Correction>& corrections) const {
+  const bool enable_transpose =
+      OmniboxFieldTrial::kFuzzyUrlSuggestionsTranspose.Get();
   DVLOG(1) << "FindCorrections(" << text << ", " << tolerance_schedule.limit
            << ")";
   DCHECK(corrections.empty());
@@ -341,6 +343,7 @@
 
   Step best{nullptr, INT_MAX, SIZE_MAX, INT_MAX, Correction()};
   int i = 0;
+
   // Find and return all equally-distant results as soon as distance increases
   // beyond that of first found results. Length is also considered to
   // avoid producing shorter substring texts.
@@ -426,7 +429,7 @@
 
         // Transpose. Look ahead cost can be balanced by faster
         // advancement through input text resulting in shorter search.
-        if (text.size() > step.index + 1 &&
+        if (enable_transpose && text.size() > step.index + 1 &&
             text[step.index + 1] == entry.first) {
           const auto it = entry.second->next.find(step_text_char);
           if (it != entry.second->next.end()) {
@@ -439,6 +442,7 @@
       }
     }
   }
+
   if (!pq.empty()) {
     DVLOG(1) << "quit early on step with distance " << pq.top().distance;
   }
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index d5684c3..01415bd 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -601,6 +601,11 @@
         "FuzzyUrlSuggestionsLowEndBypass",
         false);
 
+const base::FeatureParam<bool> OmniboxFieldTrial::kFuzzyUrlSuggestionsTranspose(
+    &omnibox::kOmniboxFuzzyUrlSuggestions,
+    "FuzzyUrlSuggestionsTranspose",
+    true);
+
 bool OmniboxFieldTrial::IsExperimentalKeywordModeEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kExperimentalKeywordMode);
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 1c335a8..e4d295af 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -359,6 +359,8 @@
 extern const base::FeatureParam<bool> kFuzzyUrlSuggestionsCounterfactual;
 // Indicates whether to bypass fuzzy processing when `IsLowEndDevice` is true.
 extern const base::FeatureParam<bool> kFuzzyUrlSuggestionsLowEndBypass;
+// Indicates whether to support transpose edit operations in fuzzy search.
+extern const base::FeatureParam<bool> kFuzzyUrlSuggestionsTranspose;
 
 // Simply a convenient wrapper for testing a flag. Used downstream for an
 // assortment of keyword mode experiments.
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index 9fc5f2f..68d1381 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/base64.h"
 #include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/containers/fixed_flat_map.h"
@@ -557,6 +558,7 @@
   int prefetch_index = -1;
   int prerender_index = -1;
   omnibox::GroupsInfo groups_info;
+  bool groups_info_parsed_from_proto = false;
 
   if (root_list.size() > 4u && root_list[4].is_dict()) {
     const base::Value& extras = root_list[4];
@@ -610,8 +612,17 @@
       }
     }
 
+    const auto* groups_info_string = extras.FindStringKey("google:groupsinfo");
+    std::string groups_info_decoded;
+    if (groups_info_string && !groups_info_string->empty() &&
+        base::Base64Decode(*groups_info_string, &groups_info_decoded) &&
+        !groups_info_decoded.empty()) {
+      groups_info_parsed_from_proto =
+          groups_info.ParseFromString(groups_info_decoded);
+    }
+
     const base::Value* header_texts = extras.FindDictKey("google:headertexts");
-    if (header_texts) {
+    if (!groups_info_parsed_from_proto && header_texts) {
       const base::Value* headers = header_texts->FindDictKey("a");
       if (headers) {
         for (auto it : headers->DictItems()) {
@@ -848,9 +859,15 @@
     // Assign a 0-based index to the group based on the number of groups so far.
     const int group_index = chrome_group_ids_map.size();
 
-    // Convert the server-provided group ID to one known to Chrome.
-    const auto chrome_group_id =
-        ChromeGroupIdForRemoteGroupIdAndIndex(suggestion_group_id, group_index);
+    // Convert the server-provided group ID to one known to Chrome; unless
+    // |groups_info| is parsed from a serialized proto in "google:groupsinfo",
+    // in which case server-provided group IDs are present in omnibox::GroupId.
+    // TODO(crbug.com/1343512): Simplify this logic once the server response has
+    // migrated to a serialized omnibox::GroupsInfo in "google:groupsinfo".
+    const auto chrome_group_id = groups_info_parsed_from_proto
+                                     ? suggestion_group_id
+                                     : ChromeGroupIdForRemoteGroupIdAndIndex(
+                                           suggestion_group_id, group_index);
 
     // Do not propagate the server-provided group IDs if Chrome ran out of
     // group IDs to assign or if the group ID was invalid to begin with.
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index 62405234..428bd152 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/omnibox/browser/search_suggestion_parser.h"
 
+#include "base/base64.h"
 #include "base/json/json_reader.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -12,6 +13,18 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+std::string SerializeGroupsInfo(const omnibox::GroupsInfo& groups_info) {
+  std::string serialized_groups_info;
+  groups_info.SerializeToString(&serialized_groups_info);
+  std::string encoded_groups_info;
+  base::Base64Encode(serialized_groups_info, &encoded_groups_info);
+  return encoded_groups_info;
+}
+
+}  // namespace
+
 ////////////////////////////////////////////////////////////////////////////////
 // DeserializeJsonData:
 
@@ -751,6 +764,395 @@
   }
 }
 
+TEST(SearchSuggestionParserTest, ParseSuggestionGroupInfo_FromProto) {
+  TestSchemeClassifier scheme_classifier;
+  AutocompleteInput input(u"", metrics::OmniboxEventProto::NTP_REALBOX,
+                          scheme_classifier);
+
+  {
+    omnibox::GroupsInfo groups_info;
+    auto* group_configs_map = groups_info.mutable_group_configs_map();
+    auto& group_config_1 = (*group_configs_map)
+        [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    group_config_1.set_header_text("Related Entities");
+    auto& group_config_2 = (*group_configs_map)[omnibox::GROUP_TRENDS];
+    group_config_2.set_header_text("Trending Searches");
+    group_config_2.set_visibility(omnibox::GroupConfig_Visibility_HIDDEN);
+
+    std::string json_data = R"([
+      "",
+      ["los angeles", "san diego", "las vegas", "san francisco"],
+      ["", "history", "", ""],
+      [],
+      {
+        "google:clientdata": {
+          "bpc": false,
+          "tlw": false
+        },
+        "google:headertexts":{
+          "a":{
+            "10000":"Related Entities",
+            "10001":"Trending Searches",
+            "40000":"Recent Searches"
+          },
+          "h":[10000, "10001"]
+        },
+        "google:groupsinfo": ")" +
+                            SerializeGroupsInfo(groups_info) + R"(",
+        "google:suggestdetail":[
+          {
+          },
+          {
+            "zl":10001
+          },
+          {
+            "zl":10002
+          },
+          {
+            "zl":40000
+          }
+        ],
+        "google:suggestrelevance": [607, 606, 605, 604],
+        "google:suggesttype": ["QUERY", "PERSONALIZED_QUERY", "QUERY", "QUERY"]
+      }])";
+    absl::optional<base::Value> root_val = base::JSONReader::Read(json_data);
+    ASSERT_TRUE(root_val);
+
+    SearchSuggestionParser::Results results;
+    ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults(
+        *root_val, input, scheme_classifier, /*default_result_relevance=*/400,
+        /*is_keyword_result=*/false, &results));
+
+    // Ensure suggestion groups are correctly parsed from the serialized proto.
+    ASSERT_EQ(2U, results.suggestion_groups_map.size());
+
+    const auto& group_1 =
+        results.suggestion_groups_map
+            [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    ASSERT_EQ("Related Entities", group_1.group_config.header_text());
+    ASSERT_EQ(omnibox::GroupConfig_Visibility_DEFAULT_VISIBLE,
+              group_1.group_config.visibility());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS,
+              group_1.original_group_id.value());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest1, group_1.priority);
+
+    const auto& group_2 = results.suggestion_groups_map[omnibox::GROUP_TRENDS];
+    ASSERT_EQ("Trending Searches", group_2.group_config.header_text());
+    ASSERT_EQ(omnibox::GroupConfig_Visibility_HIDDEN,
+              group_2.group_config.visibility());
+    ASSERT_EQ(omnibox::GROUP_TRENDS, group_2.original_group_id.value());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest2, group_2.priority);
+
+    // Ensure suggestion group IDs are correctly set in the suggestions.
+    ASSERT_EQ(4U, results.suggest_results.size());
+
+    ASSERT_EQ(u"los angeles", results.suggest_results[0].suggestion());
+    // This suggestion does not belong to a group.
+    ASSERT_EQ(absl::nullopt, results.suggest_results[0].suggestion_group_id());
+
+    ASSERT_EQ(u"san diego", results.suggest_results[1].suggestion());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS,
+              *results.suggest_results[1].suggestion_group_id());
+
+    ASSERT_EQ(u"las vegas", results.suggest_results[2].suggestion());
+    ASSERT_EQ(omnibox::GROUP_TRENDS,
+              *results.suggest_results[2].suggestion_group_id());
+
+    ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
+    // This suggestion belongs to an unrecognized group.
+    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+  }
+  {
+    omnibox::GroupsInfo groups_info;
+    auto* group_configs_map = groups_info.mutable_group_configs_map();
+    auto& group_config_1 = (*group_configs_map)
+        [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    group_config_1.set_header_text("Related Entities");
+    auto& group_config_2 = (*group_configs_map)[omnibox::GROUP_TRENDS];
+    group_config_2.set_header_text("Trending Searches");
+    group_config_2.set_visibility(omnibox::GroupConfig_Visibility_HIDDEN);
+    auto& group_config_3 =
+        (*group_configs_map)[omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST];
+    group_config_3.set_header_text("Recent Searches");
+
+    std::string json_data = R"([
+      "",
+      ["los angeles", "san diego", "las vegas", "san francisco"],
+      ["", "history", "", ""],
+      [],
+      {
+        "google:clientdata": {
+          "bpc": false,
+          "tlw": false
+        },
+        "google:headertexts":{
+          "a":{
+            "10000":"Related Entities",
+            "10001":"Trending Searches"
+          },
+          "h":[10000, "10001"]
+        },
+        "google:groupsinfo": ")" +
+                            SerializeGroupsInfo(groups_info) + R"(",
+        "google:suggestdetail":[
+          {
+          },
+          {
+            "zl":10002
+          },
+          {
+            "zl":10001
+          },
+          {
+          }
+        ],
+        "google:suggestrelevance": [607, 606, 605, 604],
+        "google:suggesttype": ["QUERY", "PERSONALIZED_QUERY", "QUERY", "QUERY"]
+      }])";
+    absl::optional<base::Value> root_val = base::JSONReader::Read(json_data);
+    ASSERT_TRUE(root_val);
+
+    SearchSuggestionParser::Results results;
+    ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults(
+        *root_val, input, scheme_classifier, /*default_result_relevance=*/400,
+        /*is_keyword_result=*/false, &results));
+
+    // Ensure suggestion groups are correctly parsed from the serialized proto.
+    ASSERT_EQ(3U, results.suggestion_groups_map.size());
+
+    const auto& group_1 = results.suggestion_groups_map[omnibox::GROUP_TRENDS];
+    ASSERT_EQ("Trending Searches", group_1.group_config.header_text());
+    ASSERT_EQ(omnibox::GroupConfig_Visibility_HIDDEN,
+              group_1.group_config.visibility());
+    ASSERT_EQ(omnibox::GROUP_TRENDS, group_1.original_group_id.value());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest1, group_1.priority);
+
+    const auto& group_2 =
+        results.suggestion_groups_map
+            [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    ASSERT_EQ("Related Entities", group_2.group_config.header_text());
+    ASSERT_EQ(omnibox::GroupConfig_Visibility_DEFAULT_VISIBLE,
+              group_2.group_config.visibility());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS,
+              group_2.original_group_id.value());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest2, group_2.priority);
+
+    const auto& group_3 =
+        results.suggestion_groups_map[omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST];
+    ASSERT_EQ("Recent Searches", group_3.group_config.header_text());
+    ASSERT_EQ(omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST,
+              group_3.original_group_id.value());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest3, group_3.priority);
+
+    // Ensure suggestion group IDs are correctly set in the suggestions.
+    ASSERT_EQ(4U, results.suggest_results.size());
+
+    ASSERT_EQ(u"los angeles", results.suggest_results[0].suggestion());
+    // This suggestion does not belong to a group.
+    ASSERT_EQ(absl::nullopt, results.suggest_results[0].suggestion_group_id());
+
+    ASSERT_EQ(u"san diego", results.suggest_results[1].suggestion());
+    ASSERT_EQ(omnibox::GROUP_TRENDS,
+              *results.suggest_results[1].suggestion_group_id());
+
+    ASSERT_EQ(u"las vegas", results.suggest_results[2].suggestion());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS,
+              *results.suggest_results[2].suggestion_group_id());
+
+    ASSERT_EQ(u"san francisco", results.suggest_results[3].suggestion());
+    // This suggestion does not belong to a group.
+    ASSERT_EQ(absl::nullopt, results.suggest_results[3].suggestion_group_id());
+  }
+  {
+    omnibox::GroupsInfo groups_info;
+    auto* group_configs_map = groups_info.mutable_group_configs_map();
+    auto& group_config_1 =
+        (*group_configs_map)[omnibox::GROUP_PREVIOUS_SEARCH_RELATED];
+    group_config_1.set_header_text("Related Searches");
+    auto& group_config_2 = (*group_configs_map)
+        [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    group_config_2.set_header_text("Related Entities");
+    auto& group_config_3 = (*group_configs_map)[omnibox::GROUP_TRENDS];
+    group_config_3.set_header_text("Trending Searches");
+    auto& group_config_4 =
+        (*group_configs_map)[omnibox::GROUP_TRENDS_ENTITY_CHIPS];
+    group_config_4.set_header_text("Trending Entities");
+    auto& group_config_5 = (*group_configs_map)[omnibox::GROUP_RELATED_QUERIES];
+    group_config_5.set_header_text("Related Questions");
+    auto& group_config_6 =
+        (*group_configs_map)[omnibox::GROUP_VISITED_DOC_RELATED];
+    group_config_6.set_header_text("Related To Websites");
+    auto& group_config_7 =
+        (*group_configs_map)[omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST];
+    group_config_7.set_header_text("Recent Searches");
+    auto& group_config_8 =
+        (*group_configs_map)[omnibox::GROUP_POLARIS_RESERVED_MAX];
+    group_config_8.set_header_text("Uknown Group");
+
+    std::string json_data = R"([
+    "",
+    [
+      "1",
+      "2",
+      "3",
+      "4",
+      "5",
+      "6",
+      "7",
+      "8"
+    ],
+    [
+      "",
+      "",
+      "",
+      "",
+      "",
+      "",
+      "",
+      ""
+    ],
+    [],
+    {
+      "google:clientdata":{
+        "bpc":false,
+        "tlw":false
+      },
+      "google:groupsinfo": ")" +
+                            SerializeGroupsInfo(groups_info) + R"(",
+      "google:suggestdetail":[
+        {
+          "zl":10000
+        },
+        {
+          "zl":10001
+        },
+        {
+          "zl":10002
+        },
+        {
+          "zl":10003
+        },
+        {
+          "zl":10004
+        },
+        {
+          "zl":10005
+        },
+        {
+          "zl":40000
+        },
+        {
+        }
+      ],
+      "google:suggestrelevance":[
+        611,
+        610,
+        609,
+        608,
+        607,
+        606,
+        605,
+        604
+      ],
+      "google:suggesttype":[
+        "QUERY",
+        "QUERY",
+        "QUERY",
+        "QUERY",
+        "QUERY",
+        "QUERY",
+        "QUERY",
+        "QUERY"
+      ]
+    }])";
+    absl::optional<base::Value> root_val = base::JSONReader::Read(json_data);
+    ASSERT_TRUE(root_val);
+
+    SearchSuggestionParser::Results results;
+    ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults(
+        *root_val, input, scheme_classifier, /*default_result_relevance=*/400,
+        /*is_keyword_result=*/false, &results));
+
+    // Ensure suggestion groups are correctly parsed from the serialized proto.
+    ASSERT_EQ(8U, results.suggestion_groups_map.size());
+
+    const auto& group_1 =
+        results.suggestion_groups_map[omnibox::GROUP_PREVIOUS_SEARCH_RELATED];
+    ASSERT_EQ("Related Searches", group_1.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest1, group_1.priority);
+
+    const auto& group_2 =
+        results.suggestion_groups_map
+            [omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS];
+    ASSERT_EQ("Related Entities", group_2.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest2, group_2.priority);
+
+    const auto& group_3 = results.suggestion_groups_map[omnibox::GROUP_TRENDS];
+    ASSERT_EQ("Trending Searches", group_3.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest3, group_3.priority);
+
+    const auto& group_4 =
+        results.suggestion_groups_map[omnibox::GROUP_TRENDS_ENTITY_CHIPS];
+    ASSERT_EQ("Trending Entities", group_4.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest4, group_4.priority);
+
+    const auto& group_5 =
+        results.suggestion_groups_map[omnibox::GROUP_RELATED_QUERIES];
+    ASSERT_EQ("Related Questions", group_5.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest5, group_5.priority);
+
+    const auto& group_6 =
+        results.suggestion_groups_map[omnibox::GROUP_VISITED_DOC_RELATED];
+    ASSERT_EQ("Related To Websites", group_6.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest6, group_6.priority);
+
+    const auto& group_7 =
+        results.suggestion_groups_map[omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST];
+    ASSERT_EQ("Recent Searches", group_7.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest7, group_7.priority);
+
+    const auto& group_8 =
+        results.suggestion_groups_map[omnibox::GROUP_POLARIS_RESERVED_MAX];
+    ASSERT_EQ("Uknown Group", group_8.group_config.header_text());
+    ASSERT_EQ(SuggestionGroupPriority::kRemoteZeroSuggest8, group_8.priority);
+
+    // Ensure suggestion group IDs are correctly set in the suggestions.
+    ASSERT_EQ(8U, results.suggest_results.size());
+
+    ASSERT_EQ(u"1", results.suggest_results[0].suggestion());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED,
+              *results.suggest_results[0].suggestion_group_id());
+
+    ASSERT_EQ(u"2", results.suggest_results[1].suggestion());
+    ASSERT_EQ(omnibox::GROUP_PREVIOUS_SEARCH_RELATED_ENTITY_CHIPS,
+              *results.suggest_results[1].suggestion_group_id());
+
+    ASSERT_EQ(u"3", results.suggest_results[2].suggestion());
+    ASSERT_EQ(omnibox::GROUP_TRENDS,
+              *results.suggest_results[2].suggestion_group_id());
+
+    ASSERT_EQ(u"4", results.suggest_results[3].suggestion());
+    ASSERT_EQ(omnibox::GROUP_TRENDS_ENTITY_CHIPS,
+              *results.suggest_results[3].suggestion_group_id());
+
+    ASSERT_EQ(u"5", results.suggest_results[4].suggestion());
+    ASSERT_EQ(omnibox::GROUP_RELATED_QUERIES,
+              *results.suggest_results[4].suggestion_group_id());
+
+    ASSERT_EQ(u"6", results.suggest_results[5].suggestion());
+    ASSERT_EQ(omnibox::GROUP_VISITED_DOC_RELATED,
+              *results.suggest_results[5].suggestion_group_id());
+
+    ASSERT_EQ(u"7", results.suggest_results[6].suggestion());
+    ASSERT_EQ(omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST,
+              *results.suggest_results[6].suggestion_group_id());
+
+    ASSERT_EQ(u"8", results.suggest_results[7].suggestion());
+    // This suggestion does not belong to a group.
+    ASSERT_EQ(absl::nullopt, results.suggest_results[7].suggestion_group_id());
+  }
+}
+
 TEST(SearchSuggestionParserTest, ParseValidSubtypes) {
   std::string json_data = R"([
       "",
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 32c047a..9328b1d 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -832,11 +832,7 @@
     CloudPolicyClient::StatusCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(is_registered());
-  // This request only works with an OAuth token identifying a user, because
-  // DMServer will resolve that user and check if they have permissions to
-  // update the device's attributes.
-  DCHECK(auth.has_oauth_token());
-
+  DCHECK(auth.has_oauth_token() || auth.has_dm_token());
   const bool has_oauth_token = auth.has_oauth_token();
   const std::string oauth_token =
       has_oauth_token ? auth.oauth_token() : std::string();
@@ -846,7 +842,7 @@
               TYPE_ATTRIBUTE_UPDATE_PERMISSION,
           this,
           /*critical=*/false,
-          !has_oauth_token ? std::move(auth) : DMAuth::NoAuth(), oauth_token,
+          !has_oauth_token ? auth.Clone() : DMAuth::NoAuth(), oauth_token,
           base::BindOnce(
               &CloudPolicyClient::OnDeviceAttributeUpdatePermissionCompleted,
               weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -863,7 +859,7 @@
     CloudPolicyClient::StatusCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(is_registered());
-  DCHECK(auth.has_oauth_token() || auth.has_enrollment_token());
+  DCHECK(auth.has_oauth_token() || auth.has_dm_token());
 
   const bool has_oauth_token = auth.has_oauth_token();
   const std::string oauth_token =
@@ -873,7 +869,8 @@
           DeviceManagementService::JobConfiguration::TYPE_ATTRIBUTE_UPDATE,
           this,
           /*critical=*/false,
-          !has_oauth_token ? std::move(auth) : DMAuth::NoAuth(), oauth_token,
+          !has_oauth_token ? auth.Clone() : DMAuth::NoAuth(),
+          oauth_token,
           base::BindOnce(&CloudPolicyClient::OnDeviceAttributeUpdated,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 
@@ -1354,7 +1351,6 @@
               ATTRIBUTE_UPDATE_ALLOWED) {
     success = true;
   }
-
   std::move(callback).Run(success);
   RemoveJob(result.job);
 }
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index f606a22..c2116e1 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -2354,7 +2354,7 @@
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 }
 
-TEST_F(CloudPolicyClientTest, RequestDeviceAttributeUpdatePermission) {
+TEST_F(CloudPolicyClientTest, RequestDeviceAttributeUpdatePermissionWithOAuthToken) {
   RegisterClient();
 
   em::DeviceManagementRequest attribute_update_permission_request;
@@ -2386,6 +2386,65 @@
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 }
 
+TEST_F(CloudPolicyClientTest, RequestDeviceAttributeUpdatePermissionWithDMToken) {
+  RegisterClient();
+
+  em::DeviceManagementRequest attribute_update_permission_request;
+  attribute_update_permission_request
+      .mutable_device_attribute_update_permission_request();
+
+  em::DeviceManagementResponse attribute_update_permission_response;
+  attribute_update_permission_response
+      .mutable_device_attribute_update_permission_response()
+      ->set_result(
+          em::DeviceAttributeUpdatePermissionResponse_ResultType_ATTRIBUTE_UPDATE_ALLOWED);
+
+  ExpectAndCaptureJob(attribute_update_permission_response);
+  EXPECT_CALL(callback_observer_, OnCallbackComplete(true)).Times(1);
+
+  CloudPolicyClient::StatusCallback callback =
+      base::BindOnce(&MockStatusCallbackObserver::OnCallbackComplete,
+                     base::Unretained(&callback_observer_));
+  client_->GetDeviceAttributeUpdatePermission(
+      DMAuth::FromDMToken(kDMToken), std::move(callback));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(DeviceManagementService::JobConfiguration::
+                TYPE_ATTRIBUTE_UPDATE_PERMISSION,
+            job_type_);
+  EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
+  EXPECT_EQ(job_request_.SerializePartialAsString(),
+            attribute_update_permission_request.SerializePartialAsString());
+  EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+}
+
+TEST_F(CloudPolicyClientTest, RequestDeviceAttributeUpdatePermissionMissingResponse) {
+  RegisterClient();
+
+  em::DeviceManagementRequest attribute_update_permission_request;
+  attribute_update_permission_request
+      .mutable_device_attribute_update_permission_request();
+
+  em::DeviceManagementResponse attribute_update_permission_response;
+
+  ExpectAndCaptureJob(attribute_update_permission_response);
+  EXPECT_CALL(callback_observer_, OnCallbackComplete(false)).Times(1);
+
+  CloudPolicyClient::StatusCallback callback =
+      base::BindOnce(&MockStatusCallbackObserver::OnCallbackComplete,
+                     base::Unretained(&callback_observer_));
+  client_->GetDeviceAttributeUpdatePermission(
+      DMAuth::FromOAuthToken(kOAuthToken), std::move(callback));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(DeviceManagementService::JobConfiguration::
+                TYPE_ATTRIBUTE_UPDATE_PERMISSION,
+            job_type_);
+  EXPECT_EQ(auth_data_, DMAuth::NoAuth());
+  VerifyQueryParameter();
+  EXPECT_EQ(job_request_.SerializePartialAsString(),
+            attribute_update_permission_request.SerializePartialAsString());
+  EXPECT_EQ(DM_STATUS_RESPONSE_DECODING_ERROR, client_->status());
+}
+
 TEST_F(CloudPolicyClientTest, RequestDeviceAttributeUpdate) {
   RegisterClient();
 
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
index 61fa147..220a75190 100644
--- a/components/printing/common/print.mojom
+++ b/components/printing/common/print.mojom
@@ -250,9 +250,12 @@
 
   // Tell the browser print preview found the selected printer has invalid
   // settings (which typically caused by disconnected network printer or
-  // printer driver is bogus).
+  // printer driver is bogus).  Extra information about the error provided in
+  // `details` can assist with debugging.
   [EnableIf=enable_print_preview]
-  PrinterSettingsInvalid(int32 document_cookie, int32 request_id);
+  PrinterSettingsInvalid(int32 document_cookie,
+                         int32 request_id,
+                         string details);
 
   // Notifies the browser of the default page layout according to the currently
   // selected printer and page size.
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 7e5d1f7..dd02301 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -12,6 +12,7 @@
 #include <string>
 #include <tuple>
 #include <utility>
+#include <vector>
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -27,6 +28,7 @@
 #include "base/run_loop.h"
 #include "base/strings/escape.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -143,13 +145,34 @@
 #endif  // BUILDFLAG(IS_APPLE)
 }
 
-bool PrintMsg_Print_Params_IsValid(const mojom::PrintParams& params) {
+bool PrintMsgPrintParamsIsValid(const mojom::PrintParams& params) {
   return !params.content_size.IsEmpty() && !params.page_size.IsEmpty() &&
          !params.printable_area.IsEmpty() && params.document_cookie &&
          params.dpi.width() > kMinDpi && params.dpi.height() > kMinDpi &&
          params.margin_top >= 0 && params.margin_left >= 0;
 }
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+std::string PrintMsgPrintParamsErrorDetails(const mojom::PrintParams& params) {
+  std::vector<base::StringPiece> details;
+
+  if (params.content_size.IsEmpty())
+    details.push_back("content size is empty");
+  if (params.page_size.IsEmpty())
+    details.push_back("page size is empty");
+  if (params.printable_area.IsEmpty())
+    details.push_back("printable area is empty");
+  if (!params.document_cookie)
+    details.push_back("invalid document cookie");
+  if (params.dpi.width() <= kMinDpi || params.dpi.height() <= kMinDpi)
+    details.push_back("invalid DPI dimensions");
+  if (params.margin_top < 0 || params.margin_left < 0)
+    details.push_back("invalid margins");
+
+  return base::JoinString(details, "; ");
+}
+#endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+
 // Helper function to check for fit to page
 bool IsPrintScalingOptionFitToPage(const mojom::PrintParams& params) {
   return params.print_scaling_option ==
@@ -2203,7 +2226,8 @@
       break;
     case INVALID_SETTINGS:
       if (preview_ui_)
-        preview_ui_->PrinterSettingsInvalid(cookie, request_id);
+        preview_ui_->PrinterSettingsInvalid(
+            cookie, request_id, print_preview_context_.last_error_details());
       print_preview_context_.Failed(false);
       break;
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -2392,7 +2416,7 @@
   // can safely assume there are no printer drivers configured. So we safely
   // terminate.
   bool result = true;
-  if (!PrintMsg_Print_Params_IsValid(*settings.params))
+  if (!PrintMsgPrintParamsIsValid(*settings.params))
     result = false;
 
   // Reset to default values.
@@ -2499,10 +2523,12 @@
 
   SetPrintPagesParams(*settings);
 
-  if (PrintMsg_Print_Params_IsValid(*settings->params))
+  if (PrintMsgPrintParamsIsValid(*settings->params))
     return true;
 
   print_preview_context_.set_error(PREVIEW_ERROR_INVALID_PRINTER_SETTINGS);
+  print_preview_context_.set_error_details(
+      PrintMsgPrintParamsErrorDetails(*settings->params));
   return false;
 }
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@@ -3054,6 +3080,11 @@
   error_ = error;
 }
 
+void PrintRenderFrameHelper::PrintPreviewContext::set_error_details(
+    const std::string& details) {
+  error_details_ = details;
+}
+
 blink::WebLocalFrame*
 PrintRenderFrameHelper::PrintPreviewContext::source_frame() {
   DCHECK(state_ != UNINITIALIZED);
@@ -3110,12 +3141,18 @@
   return error_;
 }
 
+const std::string&
+PrintRenderFrameHelper::PrintPreviewContext::last_error_details() const {
+  return error_details_;
+}
+
 void PrintRenderFrameHelper::PrintPreviewContext::ClearContext() {
   prep_frame_view_.reset();
   metafile_.reset();
   typeface_content_info_.clear();
   pages_to_render_.clear();
   error_ = PREVIEW_ERROR_NONE;
+  error_details_ = std::string();
 }
 
 void PrintRenderFrameHelper::PrintPreviewContext::CalculatePluginAttributes() {
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index fb7fe06..0f291a40 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -574,6 +574,7 @@
     void SetIsForArc(bool is_for_arc);
 #endif
     void set_error(enum PrintPreviewErrorBuckets error);
+    void set_error_details(const std::string& details);
 
     // Getters
     // Original frame for which preview was requested.
@@ -594,6 +595,7 @@
     MetafileSkia* metafile();
     ContentProxySet* typeface_content_info();
     int last_error() const;
+    const std::string& last_error_details() const;
 
    private:
     enum State {
@@ -647,6 +649,7 @@
     base::TimeTicks begin_time_;
 
     enum PrintPreviewErrorBuckets error_ = PREVIEW_ERROR_NONE;
+    std::string error_details_;
 
     State state_ = UNINITIALIZED;
   };
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index 5b1e6b57..8355f83a 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -234,7 +234,8 @@
     RunQuitClosure();
   }
   void PrinterSettingsInvalid(int32_t document_cookie,
-                              int32_t request_id) override {
+                              int32_t request_id,
+                              const std::string& details) override {
     DCHECK_EQ(preview_status_, PreviewStatus::kNone);
     preview_status_ = PreviewStatus::kInvalidSetting;
     RunQuitClosure();
diff --git a/components/quirks/quirks_client.cc b/components/quirks/quirks_client.cc
index 6308e5f6..52638028 100644
--- a/components/quirks/quirks_client.cc
+++ b/components/quirks/quirks_client.cc
@@ -189,16 +189,19 @@
 }
 
 bool QuirksClient::ParseResult(const std::string& result, std::string* data) {
-  std::string data64;
-  const base::DictionaryValue* dict;
-  std::unique_ptr<base::Value> json = base::JSONReader::ReadDeprecated(result);
-  if (!json || !json->GetAsDictionary(&dict) ||
-      !dict->GetString("icc", &data64)) {
+  absl::optional<base::Value> maybe_json = base::JSONReader::Read(result);
+  if (!maybe_json || !maybe_json->is_dict()) {
     VLOG(1) << "Failed to parse JSON icc data";
     return false;
   }
 
-  if (!base::Base64Decode(data64, data)) {
+  std::string* data64 = maybe_json->GetDict().FindString("icc");
+  if (!data64) {
+    VLOG(1) << "Missing icc data";
+    return false;
+  }
+
+  if (!base::Base64Decode(*data64, data)) {
     VLOG(1) << "Failed to decode Base64 icc data";
     return false;
   }
diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
index d060ad6..6635172 100644
--- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
+++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
@@ -7,7 +7,6 @@
 #include <string>
 
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/input_context.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 
@@ -20,9 +19,6 @@
 void DummySegmentationPlatformService::GetSelectedSegment(
     const std::string& segmentation_key,
     SegmentSelectionCallback callback) {
-  stats::RecordSegmentSelectionFailure(
-      segmentation_key,
-      stats::SegmentationSelectionFailureReason::kPlatformDisabled);
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), SegmentSelectionResult()));
 }
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index d3f1b75..d3a8bf7 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -188,8 +188,7 @@
   if (!success) {
     for (const auto& config : configs_) {
       stats::RecordSegmentSelectionFailure(
-          config->segmentation_key,
-          stats::SegmentationSelectionFailureReason::kDBInitFailure);
+          *config, stats::SegmentationSelectionFailureReason::kDBInitFailure);
     }
     return;
   }
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index 858f589e..739d743 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -110,14 +110,14 @@
     selected_segment_last_session_.is_ready = true;
     selected_segment_last_session_.rank = selected_segment->rank;
     stats::RecordSegmentSelectionFailure(
-        config_->segmentation_key,
+        *config_,
         stats::SegmentationSelectionFailureReason::kSelectionAvailableInPrefs);
 
     group_name = config_->GetSegmentUmaName(selected_segment->segment_id);
   } else {
     stats::RecordSegmentSelectionFailure(
-        config_->segmentation_key, stats::SegmentationSelectionFailureReason::
-                                       kInvalidSelectionResultInPrefs);
+        *config_, stats::SegmentationSelectionFailureReason::
+                      kInvalidSelectionResultInPrefs);
     group_name = "Unselected";
   }
 
@@ -197,7 +197,7 @@
     if (!platform_options_.force_refresh_results &&
         previous_selection->selection_time + ttl_to_use > clock_->Now()) {
       stats::RecordSegmentSelectionFailure(
-          config_->segmentation_key,
+          *config_,
           stats::SegmentationSelectionFailureReason::kSelectionTtlNotExpired);
       VLOG(1) << __func__ << ": previous selection of segment="
               << SegmentId_Name(previous_selection->segment_id)
@@ -263,7 +263,7 @@
     SegmentId current_segment_id,
     std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
   if (!result->rank) {
-    stats::RecordSegmentSelectionFailure(config_->segmentation_key,
+    stats::RecordSegmentSelectionFailure(*config_,
                                          GetFailureReason(result->state));
     if (config_->on_demand_execution && !callback.is_null()) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -322,7 +322,7 @@
   }
 
   stats::RecordSegmentSelectionComputed(
-      config_->segmentation_key, new_selection,
+      *config_, new_selection,
       previous_selection.has_value()
           ? absl::make_optional(previous_selection->segment_id)
           : absl::nullopt);
diff --git a/components/segmentation_platform/internal/stats.cc b/components/segmentation_platform/internal/stats.cc
index 24b6004..136cc9a7 100644
--- a/components/segmentation_platform/internal/stats.cc
+++ b/components/segmentation_platform/internal/stats.cc
@@ -283,19 +283,19 @@
 }
 
 void RecordSegmentSelectionComputed(
-    const std::string& segmentation_key,
+    const Config& config,
     SegmentId new_selection,
     absl::optional<SegmentId> previous_selection) {
   // Special case adaptive toolbar since it already has histograms being
   // recorded and updating names will affect current work.
-  if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
+  if (config.segmentation_key == kAdaptiveToolbarSegmentationKey) {
     base::UmaHistogramEnumeration(
         "SegmentationPlatform.AdaptiveToolbar.SegmentSelection.Computed",
         OptimizationTargetToAdaptiveToolbarButtonVariant(new_selection));
   }
-  std::string computed_hist = base::StrCat(
-      {"SegmentationPlatform.", SegmentationKeyToUmaName(segmentation_key),
-       ".SegmentSelection.Computed2"});
+  std::string computed_hist =
+      base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
+                    ".SegmentSelection.Computed2"});
   base::UmaHistogramEnumeration(
       computed_hist, OptimizationTargetToSegmentationModel(new_selection));
 
@@ -306,14 +306,14 @@
   if (prev_segment == new_selection)
     return;
 
-  std::string switched_hist = base::StrCat(
-      {"SegmentationPlatform.", SegmentationKeyToUmaName(segmentation_key),
-       ".SegmentSwitched"});
-  if (segmentation_key == kAdaptiveToolbarSegmentationKey) {
+  std::string switched_hist =
+      base::StrCat({"SegmentationPlatform.", config.segmentation_uma_name,
+                    ".SegmentSwitched"});
+  if (config.segmentation_key == kAdaptiveToolbarSegmentationKey) {
     base::UmaHistogramEnumeration(
         switched_hist,
         GetAdaptiveToolbarSegmentSwitch(new_selection, prev_segment));
-  } else if (IsBooleanSegment(segmentation_key)) {
+  } else if (IsBooleanSegment(config.segmentation_key)) {
     base::UmaHistogramEnumeration(
         switched_hist, GetBooleanSegmentSwitch(new_selection, prev_segment));
   }
@@ -518,11 +518,11 @@
       histogram_value_count);
 }
 
-void RecordSegmentSelectionFailure(const std::string& segmentation_key,
+void RecordSegmentSelectionFailure(const Config& config,
                                    SegmentationSelectionFailureReason reason) {
   base::UmaHistogramEnumeration(
       base::StrCat({"SegmentationPlatform.SelectionFailedReason.",
-                    SegmentationKeyToUmaName(segmentation_key)}),
+                    config.segmentation_uma_name}),
       reason);
 }
 
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index 796c2154..c0871ec 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -7,6 +7,7 @@
 
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/metadata/metadata_utils.h"
+#include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "components/segmentation_platform/public/proto/types.pb.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
@@ -56,7 +57,7 @@
 // Records the result of segment selection whenever segment selection is
 // computed.
 void RecordSegmentSelectionComputed(
-    const std::string& segmentation_key,
+    const Config& config,
     SegmentId new_selection,
     absl::optional<SegmentId> previous_selection);
 
@@ -157,7 +158,7 @@
 // "SegmentationSelectionFailureReason" in
 // //tools/metrics/histograms/enums.xml.
 enum class SegmentationSelectionFailureReason {
-  kPlatformDisabled = 0,
+  kDeprecatedPlatformDisabled = 0,
   kSelectionAvailableInPrefs = 1,
   kAtLeastOneSegmentNotReady = 2,
   kAtLeastOneSegmentSignalsNotCollected = 3,
@@ -177,7 +178,7 @@
 };
 
 // Records the reason for failure or success to compute a segment selection.
-void RecordSegmentSelectionFailure(const std::string& segmentation_key,
+void RecordSegmentSelectionFailure(const Config& config,
                                    SegmentationSelectionFailureReason reason);
 
 // Keep in sync with SegmentationPlatformFeatureProcessingError in
diff --git a/components/segmentation_platform/internal/stats_unittest.cc b/components/segmentation_platform/internal/stats_unittest.cc
index 3a19115..db2b7aa6 100644
--- a/components/segmentation_platform/internal/stats_unittest.cc
+++ b/components/segmentation_platform/internal/stats_unittest.cc
@@ -5,7 +5,7 @@
 #include "components/segmentation_platform/internal/stats.h"
 
 #include "base/test/metrics/histogram_tester.h"
-#include "components/segmentation_platform/public/config.h"
+#include "components/segmentation_platform/public/constants.h"
 #include "components/segmentation_platform/public/proto/segmentation_platform.pb.h"
 #include "components/segmentation_platform/public/proto/types.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -67,22 +67,23 @@
 TEST(StatsTest, AdaptiveToolbarSegmentSwitch) {
   std::string histogram("SegmentationPlatform.AdaptiveToolbar.SegmentSwitched");
   base::HistogramTester tester;
+  Config config;
+  config.segmentation_key = kAdaptiveToolbarSegmentationKey;
+  config.segmentation_uma_name =
+      SegmentationKeyToUmaName(config.segmentation_key);
 
   // Share -> New tab.
   RecordSegmentSelectionComputed(
-      kAdaptiveToolbarSegmentationKey,
-      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
+      config, SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
 
   // None -> Share.
   RecordSegmentSelectionComputed(
-      kAdaptiveToolbarSegmentationKey,
-      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, absl::nullopt);
+      config, SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE, absl::nullopt);
 
   // Share -> Share.
   RecordSegmentSelectionComputed(
-      kAdaptiveToolbarSegmentationKey,
-      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
+      config, SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
   tester.ExpectTotalCount(histogram, 2);
 
@@ -102,11 +103,14 @@
   std::string histogram(
       "SegmentationPlatform.ChromeStartAndroid.SegmentSwitched");
   base::HistogramTester tester;
+  Config config;
+  config.segmentation_key = kChromeStartAndroidSegmentationKey;
+  config.segmentation_uma_name =
+      SegmentationKeyToUmaName(config.segmentation_key);
 
   // Start to none.
   RecordSegmentSelectionComputed(
-      kChromeStartAndroidSegmentationKey,
-      SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
+      config, SegmentId::OPTIMIZATION_TARGET_UNKNOWN,
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID);
 
   tester.ExpectTotalCount(histogram, 1);
@@ -115,8 +119,7 @@
                   static_cast<int>(BooleanSegmentSwitch::kEnabledToNone), 1)));
   // None to start.
   RecordSegmentSelectionComputed(
-      kChromeStartAndroidSegmentationKey,
-      SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
+      config, SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
       absl::nullopt);
 
   tester.ExpectTotalCount(histogram, 2);
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index 56b85ec..45d14c5 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -837,22 +837,26 @@
   if (!dict)
     return nullptr;
 
-  auto action = GetStringValueFromDict(*dict, kActionKey);
+  return ConvertDictToIntent(*dict);
+}
+
+apps::IntentPtr ConvertDictToIntent(const base::Value::Dict& dict) {
+  auto action = GetStringValueFromDict(dict, kActionKey);
   if (!action.has_value())
     return nullptr;
   auto intent = std::make_unique<apps::Intent>(action.value());
-  intent->url = GetGurlValueFromDict(*dict, kUrlKey);
-  intent->mime_type = GetStringValueFromDict(*dict, kMimeTypeKey);
-  intent->files = GetFilesFromDict(*dict, kFileUrlsKey);
-  intent->activity_name = GetStringValueFromDict(*dict, kActivityNameKey);
-  intent->drive_share_url = GetGurlValueFromDict(*dict, kDriveShareUrlKey);
-  intent->share_text = GetStringValueFromDict(*dict, kShareTextKey);
-  intent->share_title = GetStringValueFromDict(*dict, kShareTitleKey);
-  intent->start_type = GetStringValueFromDict(*dict, kStartTypeKey);
-  intent->categories = GetCategoriesFromDict(*dict, kCategoriesKey);
-  intent->data = GetStringValueFromDict(*dict, kDataKey);
-  intent->ui_bypassed = GetBoolValueFromDict(*dict, kUiBypassedKey);
-  intent->extras = GetExtrasFromDict(*dict, kExtrasKey);
+  intent->url = GetGurlValueFromDict(dict, kUrlKey);
+  intent->mime_type = GetStringValueFromDict(dict, kMimeTypeKey);
+  intent->files = GetFilesFromDict(dict, kFileUrlsKey);
+  intent->activity_name = GetStringValueFromDict(dict, kActivityNameKey);
+  intent->drive_share_url = GetGurlValueFromDict(dict, kDriveShareUrlKey);
+  intent->share_text = GetStringValueFromDict(dict, kShareTextKey);
+  intent->share_title = GetStringValueFromDict(dict, kShareTitleKey);
+  intent->start_type = GetStringValueFromDict(dict, kStartTypeKey);
+  intent->categories = GetCategoriesFromDict(dict, kCategoriesKey);
+  intent->data = GetStringValueFromDict(dict, kDataKey);
+  intent->ui_bypassed = GetBoolValueFromDict(dict, kUiBypassedKey);
+  intent->extras = GetExtrasFromDict(dict, kExtrasKey);
 
   return intent;
 }
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h
index 81457ed..e03cf4ad 100644
--- a/components/services/app_service/public/cpp/intent_util.h
+++ b/components/services/app_service/public/cpp/intent_util.h
@@ -9,17 +9,13 @@
 
 #include <string>
 
+#include "base/values.h"
 #include "components/services/app_service/public/cpp/intent.h"
 #include "components/services/app_service/public/cpp/intent_filter.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
-namespace base {
-class DictionaryValue;
-class Value;
-}  // namespace base
-
 namespace apps_util {
 
 extern const char kIntentActionMain[];
@@ -222,7 +218,7 @@
 // Gets the string value from base::DictionaryValue, e.g. { "key": "value" }
 // returns "value".
 absl::optional<std::string> GetStringValueFromDict(
-    const base::DictionaryValue& dict,
+    const base::Value::Dict& dict,
     const std::string& key_name);
 
 // Gets absl::optional<bool> value from base::DictionaryValue, e.g. {
@@ -252,6 +248,7 @@
 
 // Converts base::Value to Intent. Returns nullptr for invalid base::Values.
 apps::IntentPtr ConvertValueToIntent(base::Value&& value);
+apps::IntentPtr ConvertDictToIntent(const base::Value::Dict& dict);
 
 // Calculates the least general mime type that matches all of the given ones.
 // E.g., for ["image/jpeg", "image/png"] it will be "image/*". ["text/html",
diff --git a/components/services/storage/public/mojom/buckets/BUILD.gn b/components/services/storage/public/mojom/buckets/BUILD.gn
index ccda650..fca88de 100644
--- a/components/services/storage/public/mojom/buckets/BUILD.gn
+++ b/components/services/storage/public/mojom/buckets/BUILD.gn
@@ -11,9 +11,13 @@
   ]
   public_deps = [
     "//mojo/public/mojom/base",
-    "//third_party/blink/public/mojom:mojom_platform",
+    "//third_party/blink/public/mojom/quota",
+    "//third_party/blink/public/mojom/storage_key",
   ]
-  overridden_deps = [ "//third_party/blink/public/mojom:mojom_platform" ]
+  overridden_deps = [
+    "//third_party/blink/public/mojom/quota",
+    "//third_party/blink/public/mojom/storage_key",
+  ]
   component_deps = [ "//third_party/blink/public/common" ]
   cpp_typemaps = [
     {
@@ -41,4 +45,6 @@
           [ "//components/services/storage/public/cpp/buckets" ]
     },
   ]
+
+  webui_module_path = "/"
 }
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc
index e2d6be5..088cc236 100644
--- a/components/translate/core/browser/translate_language_list.cc
+++ b/components/translate/core/browser/translate_language_list.cc
@@ -38,6 +38,7 @@
 // This list must be sorted in alphabetical order and contain no duplicates.
 const char* const kDefaultSupportedLanguages[] = {
     "af",     // Afrikaans
+    "ak",     // Twi
     "am",     // Amharic
     "ar",     // Arabic
     "az",     // Azerbaijani
@@ -47,11 +48,13 @@
     "bs",     // Bosnian
     "ca",     // Catalan
     "ceb",    // Cebuano
+    "ckb",    // Kurdish (Sorani)
     "co",     // Corsican
     "cs",     // Czech
     "cy",     // Welsh
     "da",     // Danish
     "de",     // German
+    "ee",     // Ewe
     "el",     // Greek
     "en",     // English
     "eo",     // Esperanto
@@ -65,6 +68,7 @@
     "ga",     // Irish
     "gd",     // Scots Gaelic
     "gl",     // Galician
+    "gom",    // Konkani
     "gu",     // Gujarati
     "ha",     // Hausa
     "haw",    // Hawaiian
@@ -86,10 +90,13 @@
     "km",     // Khmer
     "kn",     // Kannada
     "ko",     // Korean
+    "kri",    // Krio
     "ku",     // Kurdish
     "ky",     // Kyrgyz
     "la",     // Latin
     "lb",     // Luxembourgish
+    "lg",     // Luganda
+    "ln",     // Lingala
     "lo",     // Lao
     "lt",     // Lithuanian
     "lv",     // Latvian
@@ -105,12 +112,15 @@
     "ne",     // Nepali
     "nl",     // Dutch
     "no",     // Norwegian - Chrome uses "nb"
+    "nso",    // Sepedi
     "ny",     // Nyanja
+    "om",     // Oromo
     "or",     // Odia (Oriya)
     "pa",     // Punjabi
     "pl",     // Polish
     "ps",     // Pashto
     "pt",     // Portuguese
+    "qu",     // Quechua
     "ro",     // Romanian
     "ru",     // Russian
     "rw",     // Kinyarwanda
@@ -131,6 +141,7 @@
     "te",     // Telugu
     "tg",     // Tajik
     "th",     // Thai
+    "ti",     // Tigrinya
     "tk",     // Turkmen
     "tl",     // Tagalog - Chrome uses "fil"
     "tr",     // Turkish
diff --git a/components/translate/core/browser/translate_prefs_unittest.cc b/components/translate/core/browser/translate_prefs_unittest.cc
index 9728746..096252f 100644
--- a/components/translate/core/browser/translate_prefs_unittest.cc
+++ b/components/translate/core/browser/translate_prefs_unittest.cc
@@ -238,7 +238,7 @@
   EXPECT_THAT(result_codes, expected_translatable_codes);
 
   // Test with only untranslatable languages.
-  content_languages = {"wa", "ln"};
+  content_languages = {"wa", "vo"};
   expected_translatable_codes = {};
   accept_languages_tester_->SetLanguagePrefs(content_languages);
 
diff --git a/components/translate/core/common/translate_util.cc b/components/translate/core/common/translate_util.cc
index a10be678..67d7cd7 100644
--- a/components/translate/core/common/translate_util.cc
+++ b/components/translate/core/common/translate_util.cc
@@ -53,6 +53,11 @@
 const base::FeatureParam<int> kDesktopPartialTranslateBubbleShowDelayMs{
     &kDesktopPartialTranslate, "DesktopPartialTranslateBubbleShowDelayMs", 500};
 
+#if !BUILDFLAG(IS_WIN)
+const base::Feature kMmapLanguageDetectionModel{
+    "MmapLanguageDetectionModel", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 GURL GetTranslateSecurityOrigin() {
   std::string security_origin(kSecurityOrigin);
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/components/translate/core/common/translate_util.h b/components/translate/core/common/translate_util.h
index 0a76464..deb29ed6 100644
--- a/components/translate/core/common/translate_util.h
+++ b/components/translate/core/common/translate_util.h
@@ -34,6 +34,11 @@
 // is shown.
 extern const base::FeatureParam<int> kDesktopPartialTranslateBubbleShowDelayMs;
 
+#if !BUILDFLAG(IS_WIN)
+// Controls whether mmap is used to load the language detection model.
+extern const base::Feature kMmapLanguageDetectionModel;
+#endif
+
 // Isolated world sets following security-origin by default.
 extern const char kSecurityOrigin[];
 
diff --git a/components/translate/core/language_detection/language_detection_model.cc b/components/translate/core/language_detection/language_detection_model.cc
index ee59eb5..cda52153 100644
--- a/components/translate/core/language_detection/language_detection_model.cc
+++ b/components/translate/core/language_detection/language_detection_model.cc
@@ -4,6 +4,7 @@
 
 #include "components/translate/core/language_detection/language_detection_model.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/histogram_macros_local.h"
 #include "base/strings/utf_string_conversions.h"
@@ -91,15 +92,26 @@
       ->mutable_cpu_settings()
       ->set_num_threads(num_threads_);
 
-  std::string file_content(model_file.GetLength(), '\0');
-  int bytes_read =
-      model_file.Read(0, std::data(file_content), model_file.GetLength());
-  if (bytes_read != model_file.GetLength()) {
-    return;
+// Windows doesn't support using mmap for the language detection model.
+#if !BUILDFLAG(IS_WIN)
+  if (base::FeatureList::IsEnabled(kMmapLanguageDetectionModel)) {
+    options.mutable_base_options()
+        ->mutable_model_file()
+        ->mutable_file_descriptor_meta()
+        ->set_fd(model_file.GetPlatformFile());
+  } else
+#endif
+  {
+    std::string file_content(model_file.GetLength(), '\0');
+    int bytes_read =
+        model_file.Read(0, std::data(file_content), model_file.GetLength());
+    if (bytes_read != model_file.GetLength()) {
+      return;
+    }
+    *options.mutable_base_options()
+         ->mutable_model_file()
+         ->mutable_file_content() = std::move(file_content);
   }
-  *options.mutable_base_options()
-       ->mutable_model_file()
-       ->mutable_file_content() = std::move(file_content);
 
   auto statusor_classifier =
       tflite::task::text::nlclassifier::NLClassifier::CreateFromOptions(
diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc
index e96569a2..6f8d7af 100644
--- a/components/viz/host/client_frame_sink_video_capturer.cc
+++ b/components/viz/host/client_frame_sink_video_capturer.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/ranges/algorithm.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "media/capture/mojom/video_capture_buffer.mojom.h"
 #include "media/capture/mojom/video_capture_types.mojom.h"
@@ -129,10 +130,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // If there is an existing overlay at the same index, drop it.
-  auto it = std::find_if(overlays_.begin(), overlays_.end(),
-                         [&stacking_index](const Overlay* overlay) {
-                           return overlay->stacking_index() == stacking_index;
-                         });
+  auto it =
+      base::ranges::find(overlays_, stacking_index, &Overlay::stacking_index);
   if (it != overlays_.end()) {
     (*it)->DisconnectPermanently();
     overlays_.erase(it);
@@ -243,7 +242,7 @@
 void ClientFrameSinkVideoCapturer::OnOverlayDestroyed(Overlay* overlay) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const auto it = std::find(overlays_.begin(), overlays_.end(), overlay);
+  const auto it = base::ranges::find(overlays_, overlay);
   DCHECK(it != overlays_.end());
   overlays_.erase(it);
 }
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 3682de6..c4dff5b2 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -5,6 +5,7 @@
 #include "components/viz/service/display/display.h"
 
 #include <stddef.h>
+
 #include <algorithm>
 #include <limits>
 #include <utility>
@@ -13,6 +14,7 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/trace_event.h"
@@ -212,9 +214,9 @@
 
   reduced_region->clear();
   for (gfx::Rect r : region) {
-    auto it =
-        std::find_if(reduced_region->begin(), reduced_region->end(),
-                     [&r](const gfx::Rect& a) { return a.SharesEdgeWith(r); });
+    auto it = base::ranges::find_if(*reduced_region, [&r](const gfx::Rect& a) {
+      return a.SharesEdgeWith(r);
+    });
     if (it != reduced_region->end()) {
       it->Union(r);
       continue;
diff --git a/components/viz/service/display/draw_polygon.cc b/components/viz/service/display/draw_polygon.cc
index de7556c..ab268b8 100644
--- a/components/viz/service/display/draw_polygon.cc
+++ b/components/viz/service/display/draw_polygon.cc
@@ -5,10 +5,12 @@
 #include "components/viz/service/display/draw_polygon.h"
 
 #include <stddef.h>
+
 #include <cmath>
 #include <utility>
 #include <vector>
 
+#include "base/ranges/algorithm.h"
 #include "build/build_config.h"
 #include "cc/base/math_util.h"
 #include "components/viz/common/quads/draw_quad.h"
@@ -245,15 +247,15 @@
   size_t pre_back_begin;
 
   // Find the first vertex that is part of the front split polygon.
-  front_begin = std::find_if(vertex_distance.begin(), vertex_distance.end(),
-                             [](float val) { return val > 0.0; }) -
+  front_begin = base::ranges::find_if(vertex_distance,
+                                      [](float val) { return val > 0.0; }) -
                 vertex_distance.begin();
   while (vertex_distance[pre_front_begin = prev(front_begin)] > 0.0)
     front_begin = pre_front_begin;
 
   // Find the first vertex that is part of the back split polygon.
-  back_begin = std::find_if(vertex_distance.begin(), vertex_distance.end(),
-                            [](float val) { return val < 0.0; }) -
+  back_begin = base::ranges::find_if(vertex_distance,
+                                     [](float val) { return val < 0.0; }) -
                vertex_distance.begin();
   while (vertex_distance[pre_back_begin = prev(back_begin)] < 0.0)
     back_begin = pre_back_begin;
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 3d8d463..22758869 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -15,6 +15,7 @@
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/ranges/algorithm.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -1041,21 +1042,17 @@
     // If this mailbox is for render pass overlay, mark the released render pass
     // overlay backing as available to be re-used.
     auto it =
-        std::find_if(in_flight_render_pass_overlay_backings_.begin(),
-                     in_flight_render_pass_overlay_backings_.end(),
-                     [&mailbox](const RenderPassOverlayParams& overlay) {
-                       return overlay.render_pass_backing.mailbox == mailbox;
-                     });
+        base::ranges::find(in_flight_render_pass_overlay_backings_, mailbox,
+                           [](const RenderPassOverlayParams& overlay) {
+                             return overlay.render_pass_backing.mailbox;
+                           });
     if (it != in_flight_render_pass_overlay_backings_.end()) {
       available_render_pass_overlay_backings_.push_back(*it);
       in_flight_render_pass_overlay_backings_.erase(it);
     }
 
-    auto iter = std::find_if(awaiting_release_overlay_locks_.begin(),
-                             awaiting_release_overlay_locks_.end(),
-                             [&mailbox](const OverlayLock& lock) {
-                               return lock.mailbox() == mailbox;
-                             });
+    auto iter = base::ranges::find(awaiting_release_overlay_locks_, mailbox,
+                                   &OverlayLock::mailbox);
     if (iter == awaiting_release_overlay_locks_.end()) {
 // TODO(crbug.com/1299794): Re-enable this DCHECK on Ozone.
 #if !defined(USE_OZONE)
@@ -3290,15 +3287,14 @@
     gfx::ColorSpace color_space,
     const gfx::Size& buffer_size) {
   RenderPassOverlayParams overlay_params;
-  auto it = std::find_if(available_render_pass_overlay_backings_.begin(),
-                         available_render_pass_overlay_backings_.end(),
-                         [&buffer_format, &buffer_size, &color_space](
-                             const RenderPassOverlayParams& overlay) {
-                           auto& backing = overlay.render_pass_backing;
-                           return backing.format == buffer_format &&
-                                  backing.size == buffer_size &&
-                                  backing.color_space == color_space;
-                         });
+  auto it = base::ranges::find_if(available_render_pass_overlay_backings_,
+                                  [&buffer_format, &buffer_size, &color_space](
+                                      const RenderPassOverlayParams& overlay) {
+                                    auto& backing = overlay.render_pass_backing;
+                                    return backing.format == buffer_format &&
+                                           backing.size == buffer_size &&
+                                           backing.color_space == color_space;
+                                  });
   if (it == available_render_pass_overlay_backings_.end()) {
     // Allocate the image for render pass overlay if there is no existing
     // available one.
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index 97623d8..5f3e2c5 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -7,7 +7,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <utility>
 
 #include "base/containers/adapters.h"
@@ -15,6 +14,7 @@
 #include "base/containers/queue.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
+#include "base/ranges/algorithm.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_tick_clock.h"
 #include "base/trace_event/trace_event.h"
@@ -354,11 +354,10 @@
   // Find the iterator to the range tracking entry for |surface_id|. Use that
   // iterator to find the right end iterator for the temporary references we
   // want to remove.
-  auto end_iter =
-      std::find_if(frame_sink_temp_refs.begin(), frame_sink_temp_refs.end(),
-                   [&surface_id](const LocalSurfaceId& id) {
-                     return id.IsNewerThan(surface_id.local_surface_id());
-                   });
+  auto end_iter = base::ranges::find_if(
+      frame_sink_temp_refs, [&surface_id](const LocalSurfaceId& id) {
+        return id.IsNewerThan(surface_id.local_surface_id());
+      });
   auto begin_iter = frame_sink_temp_refs.begin();
 
   // Remove temporary references and range tracking information.
diff --git a/components/web_modal/web_contents_modal_dialog_manager.cc b/components/web_modal/web_contents_modal_dialog_manager.cc
index 5d04dd8..fc383b5 100644
--- a/components/web_modal/web_contents_modal_dialog_manager.cc
+++ b/components/web_modal/web_contents_modal_dialog_manager.cc
@@ -4,10 +4,10 @@
 
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/check.h"
+#include "base/ranges/algorithm.h"
 #include "components/back_forward_cache/back_forward_cache_disable.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
 #include "content/public/browser/back_forward_cache.h"
@@ -63,10 +63,7 @@
 }
 
 void WebContentsModalDialogManager::WillClose(gfx::NativeWindow dialog) {
-  auto dlg = std::find_if(child_dialogs_.begin(), child_dialogs_.end(),
-                          [dialog](const DialogState& child_dialog) {
-                            return child_dialog.dialog == dialog;
-                          });
+  auto dlg = base::ranges::find(child_dialogs_, dialog, &DialogState::dialog);
 
   // The Views tab contents modal dialog calls WillClose twice.  Ignore the
   // second invocation.
diff --git a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
index d2822a33..0ca6ae04 100644
--- a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
+++ b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
@@ -434,9 +434,8 @@
 // compatibility.
 TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutPwa) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {}, {features::kSkipServiceWorkerCheckInstallOnly,
-           features::kSkipServiceWorkerCheckAll});
+  scoped_feature_list.InitAndDisableFeature(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
   SetManifest(BuildDefaultManifest());
   SetShouldServiceWorkerTimeOut(true);
@@ -458,9 +457,8 @@
 
 TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutNonPwa) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {}, {features::kSkipServiceWorkerCheckInstallOnly,
-           features::kSkipServiceWorkerCheckAll});
+  scoped_feature_list.InitAndDisableFeature(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
   SetManifest(BuildDefaultManifest());
   SetShouldServiceWorkerTimeOut(true);
@@ -483,9 +481,8 @@
 
 TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutUnknown) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {}, {features::kSkipServiceWorkerCheckInstallOnly,
-           features::kSkipServiceWorkerCheckAll});
+  scoped_feature_list.InitAndDisableFeature(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
   SetManifest(BuildDefaultManifest());
   SetShouldServiceWorkerTimeOut(true);
@@ -532,9 +529,8 @@
 
 TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {}, {features::kSkipServiceWorkerCheckInstallOnly,
-           features::kSkipServiceWorkerCheckAll});
+  scoped_feature_list.InitAndDisableFeature(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
   // Test that when the manifest provides Manifest::name but not
   // Manifest::short_name that Manifest::name is used as the title.
@@ -637,39 +633,10 @@
             GURL(kDefaultIconUrl));
 }
 
-TEST_F(AddToHomescreenDataFetcherTest,
-       NoServiceWorkerInstallable_InstallOnlyFlag) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {features::kSkipServiceWorkerCheckInstallOnly},
-      {features::kSkipServiceWorkerCheckAll});
-  SetManifest(BuildDefaultManifest());
-  SetHasServiceWorker(false);
+TEST_F(AddToHomescreenDataFetcherTest, NoServiceWorkerInstallable) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
-  // Check where InstallableManager doesn't finish working after the timeout.
-  // This is akin to waiting for a service worker forever.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
-             true /*is_webapk_compatible*/,
-             InstallableStatusCode::NO_ERROR_DETECTED);
-
-  // Navigate to ensure the histograms are written.
-  NavigateAndCommit(GURL("about:blank"));
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
-TEST_F(AddToHomescreenDataFetcherTest, NoServiceWorkerInstallable_SkipAllFlag) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {features::kSkipServiceWorkerCheckAll},
-      {features::kSkipServiceWorkerCheckInstallOnly});
   SetManifest(BuildDefaultManifest());
   SetHasServiceWorker(false);
 
@@ -693,11 +660,8 @@
 }
 
 TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerTimeOutInstallable) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {features::kSkipServiceWorkerCheckInstallOnly,
-       features::kSkipServiceWorkerCheckAll},
-      {});
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kSkipServiceWorkerCheckInstallOnly);
 
   SetManifest(BuildDefaultManifest());
   SetShouldServiceWorkerTimeOut(true);
diff --git a/components/webapps/browser/banners/app_banner_manager.cc b/components/webapps/browser/banners/app_banner_manager.cc
index b8f5bc83d..0264fe3 100644
--- a/components/webapps/browser/banners/app_banner_manager.cc
+++ b/components/webapps/browser/banners/app_banner_manager.cc
@@ -366,8 +366,8 @@
   InstallableParams params;
   params.valid_primary_icon = true;
   params.valid_manifest = true;
-  params.has_worker = !features::SkipBannerServiceWorkerCheck();
-  params.wait_for_worker = !features::SkipBannerServiceWorkerCheck();
+  params.has_worker = true;
+  params.wait_for_worker = true;
   params.fetch_screenshots = true;
 
   return params;
diff --git a/components/webapps/browser/features.cc b/components/webapps/browser/features.cc
index b5cc7f36..641daca 100644
--- a/components/webapps/browser/features.cc
+++ b/components/webapps/browser/features.cc
@@ -43,11 +43,6 @@
 const base::Feature kCreateShortcutIgnoresManifest{
     "CreateShortcutIgnoresManifest", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Skip the service worker in all install criteria check. This affect both
-// "intallable" and "promotable" status of a web app.
-const base::Feature kSkipServiceWorkerCheckAll{
-    "SkipServiceWorkerCheckAll", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Skip the service worker install criteria check for installing. This affect
 // only the "installable" status but not "promotable".
 const base::Feature kSkipServiceWorkerCheckInstallOnly{
@@ -63,13 +58,8 @@
 const base::Feature kDesktopPWAsDetailedInstallDialog{
     "DesktopPWAsDetailedInstallDialog", base::FEATURE_ENABLED_BY_DEFAULT};
 
-bool SkipBannerServiceWorkerCheck() {
-  return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckAll);
-}
-
 bool SkipInstallServiceWorkerCheck() {
-  return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckAll) ||
-         base::FeatureList::IsEnabled(kSkipServiceWorkerCheckInstallOnly);
+  return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckInstallOnly);
 }
 
 }  // namespace features
diff --git a/components/webapps/browser/features.h b/components/webapps/browser/features.h
index 8708e94..8e87942 100644
--- a/components/webapps/browser/features.h
+++ b/components/webapps/browser/features.h
@@ -22,11 +22,9 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 extern const base::Feature kCreateShortcutIgnoresManifest;
-extern const base::Feature kSkipServiceWorkerCheckAll;
 extern const base::Feature kSkipServiceWorkerCheckInstallOnly;
 extern const base::Feature kDesktopPWAsDetailedInstallDialog;
 
-bool SkipBannerServiceWorkerCheck();
 bool SkipInstallServiceWorkerCheck();
 
 }  // namespace features
diff --git a/components/webcrypto/algorithms/ecdh_unittest.cc b/components/webcrypto/algorithms/ecdh_unittest.cc
index 66e19ea..58db977d 100644
--- a/components/webcrypto/algorithms/ecdh_unittest.cc
+++ b/components/webcrypto/algorithms/ecdh_unittest.cc
@@ -5,6 +5,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "base/ranges/algorithm.h"
 #include "components/webcrypto/algorithm_dispatch.h"
 #include "components/webcrypto/algorithms/ec.h"
 #include "components/webcrypto/algorithms/test_helpers.h"
@@ -113,10 +114,9 @@
 // 528 bits.
 KeyPair LoadTestKeys() {
   base::Value::List tests = ReadJsonTestFileAsList("ecdh.json");
-  const auto& test = std::find_if(
-      tests.begin(), tests.end(), [](const base::Value& v) -> bool {
-        return v.GetDict().FindBool("valid_p521_keys").has_value();
-      });
+  const auto& test = base::ranges::find_if(tests, [](const base::Value& v) {
+    return v.GetDict().FindBool("valid_p521_keys").has_value();
+  });
 
   CHECK(test != tests.end()) << "test key set contains no valid P-521 keys";
 
diff --git a/components/zucchini/address_translator_unittest.cc b/components/zucchini/address_translator_unittest.cc
index d723598..5895b339 100644
--- a/components/zucchini/address_translator_unittest.cc
+++ b/components/zucchini/address_translator_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "components/zucchini/address_translator.h"
 
-#include <algorithm>
 #include <string>
 #include <utility>
 
 #include "base/format_macros.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -54,7 +54,7 @@
 
       auto first_non_blank = [](const std::string& t) {
         auto is_blank = [](char ch) { return ch == '.'; };
-        return std::find_if_not(t.begin(), t.end(), is_blank) - t.begin();
+        return base::ranges::find_if_not(t, is_blank) - t.begin();
       };
       auto count_non_special = [](const std::string& t) {
         auto is_special = [](char ch) { return ch == '.' || ch == '!'; };
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index a14d2f2..52cf0b7d 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -10,6 +10,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/version.h"
 #include "build/build_config.h"
 #include "content/browser/accessibility/accessibility_browsertest.h"
 #include "content/browser/accessibility/browser_accessibility.h"
@@ -514,54 +515,61 @@
   int x = -1, y = -1;
   int width = -1, height = -1;
 
+  // When given invalid arguments, atk_text_get_character_extents returns 0
+  // before 2.35.1 and -1 after:
+  // https://gitlab.gnome.org/GNOME/atk/-/merge_requests/44#9f621eb5fd3bcb2fa5c7bd228c9b1ad42edc46c8_32_33
+  // https://gnome.pages.gitlab.gnome.org/at-spi2-core/atk/AtkText.html#atk-text-get-character-extents
+  base::Version atk_version(atk_get_version());
+  int expect = atk_version.CompareTo(base::Version("2.35.1")) >= 0 ? -1 : 0;
+
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_SCREEN);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 
 #ifdef ATK_230
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_PARENT);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 #endif  // ATK_230
 
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_WINDOW);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 
   int n_characters = atk_text_get_character_count(atk_text);
   ASSERT_LT(0, n_characters);
 
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_SCREEN);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 
 #ifdef ATK_230
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_PARENT);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 #endif  // ATK_230
 
   atk_text_get_character_extents(atk_text, invalid_offset, &x, &y, &width,
                                  &height, ATK_XY_WINDOW);
-  EXPECT_EQ(0, x);
-  EXPECT_EQ(0, y);
-  EXPECT_EQ(0, width);
-  EXPECT_EQ(0, height);
+  EXPECT_EQ(expect, x);
+  EXPECT_EQ(expect, y);
+  EXPECT_EQ(expect, width);
+  EXPECT_EQ(expect, height);
 
   g_object_unref(atk_text);
 }
diff --git a/content/browser/aggregation_service/aggregatable_report_scheduler.cc b/content/browser/aggregation_service/aggregatable_report_scheduler.cc
index 2d980c6..ad4c80f 100644
--- a/content/browser/aggregation_service/aggregatable_report_scheduler.cc
+++ b/content/browser/aggregation_service/aggregatable_report_scheduler.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/check.h"
+#include "base/command_line.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
@@ -22,6 +23,7 @@
 #include "content/browser/aggregation_service/aggregation_service.h"
 #include "content/browser/aggregation_service/aggregation_service_storage.h"
 #include "content/browser/aggregation_service/aggregation_service_storage_context.h"
+#include "content/public/common/content_switches.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace content {
@@ -81,7 +83,10 @@
         on_scheduled_report_time_reached)
     : storage_context_(*storage_context),
       on_scheduled_report_time_reached_(
-          std::move(on_scheduled_report_time_reached)) {
+          std::move(on_scheduled_report_time_reached)),
+      should_not_delay_reports_(
+          base::CommandLine::ForCurrentProcess()->HasSwitch(
+              switches::kPrivateAggregationDebugMode)) {
   DCHECK(storage_context);
 }
 AggregatableReportScheduler::TimerDelegate::~TimerDelegate() = default;
@@ -107,6 +112,15 @@
 
 void AggregatableReportScheduler::TimerDelegate::AdjustOfflineReportTimes(
     base::OnceCallback<void(absl::optional<base::Time>)> maybe_set_timer_cb) {
+  if (should_not_delay_reports_) {
+    // No need to adjust the report times, just set the timer as appropriate.
+    storage_context_->GetStorage()
+        .AsyncCall(&AggregationServiceStorage::NextReportTimeAfter)
+        .WithArgs(base::Time::Min())
+        .Then(std::move(maybe_set_timer_cb));
+    return;
+  }
+
   storage_context_->GetStorage()
       .AsyncCall(&AggregationServiceStorage::AdjustOfflineReportTimes)
       .WithArgs(base::Time::Now(), kOfflineReportTimeMinimumDelay,
diff --git a/content/browser/aggregation_service/aggregatable_report_scheduler.h b/content/browser/aggregation_service/aggregatable_report_scheduler.h
index fbbe11e..55702b1 100644
--- a/content/browser/aggregation_service/aggregatable_report_scheduler.h
+++ b/content/browser/aggregation_service/aggregatable_report_scheduler.h
@@ -129,6 +129,9 @@
     // the typical size.
     std::set<AggregationServiceStorage::RequestId> in_progress_requests_;
 
+    // Set iff the private aggregation debug mode is set.
+    bool should_not_delay_reports_;
+
     base::WeakPtrFactory<AggregatableReportScheduler::TimerDelegate>
         weak_ptr_factory_{this};
   };
diff --git a/content/browser/aggregation_service/aggregatable_report_scheduler_unittest.cc b/content/browser/aggregation_service/aggregatable_report_scheduler_unittest.cc
index 4148343..228fa93 100644
--- a/content/browser/aggregation_service/aggregatable_report_scheduler_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_scheduler_unittest.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/containers/flat_set.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
@@ -20,6 +21,7 @@
 #include "content/browser/aggregation_service/aggregation_service.h"
 #include "content/browser/aggregation_service/aggregation_service_storage.h"
 #include "content/browser/aggregation_service/aggregation_service_test_utils.h"
+#include "content/public/common/content_switches.h"
 #include "services/network/public/mojom/network_change_manager.mojom.h"
 #include "services/network/test/test_network_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -47,9 +49,13 @@
  public:
   AggregatableReportSchedulerTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
-        storage_context_(base::DefaultClock::GetInstance()),
-        scheduler_(&storage_context_,
-                   /*on_scheduled_report_time_reached=*/mock_callback_.Get()) {}
+        storage_context_(base::DefaultClock::GetInstance()) {}
+
+  void SetUp() override {
+    scheduler_ = std::make_unique<AggregatableReportScheduler>(
+        &storage_context_,
+        /*on_scheduled_report_time_reached=*/mock_callback_.Get());
+  }
 
  protected:
   base::test::TaskEnvironment task_environment_;
@@ -57,7 +63,7 @@
   base::MockRepeatingCallback<void(
       std::vector<AggregationServiceStorage::RequestAndId>)>
       mock_callback_;
-  AggregatableReportScheduler scheduler_;
+  std::unique_ptr<AggregatableReportScheduler> scheduler_;
 };
 
 TEST_F(AggregatableReportSchedulerTest,
@@ -107,7 +113,7 @@
             }));
   }
 
-  scheduler_.ScheduleRequest(
+  scheduler_->ScheduleRequest(
       aggregation_service::CloneReportRequest(expected_request));
 
   {
@@ -160,7 +166,7 @@
       example_request.shared_info().Clone();
   expected_shared_info.scheduled_report_time = kExampleTime;
 
-  scheduler_.ScheduleRequest(
+  scheduler_->ScheduleRequest(
       AggregatableReportRequest::Create(example_request.payload_contents(),
                                         std::move(expected_shared_info))
           .value());
@@ -188,7 +194,7 @@
   }
 
   // Request IDs are incremented from 1.
-  scheduler_.NotifyInProgressRequestSucceeded(
+  scheduler_->NotifyInProgressRequestSucceeded(
       AggregationServiceStorage::RequestId(1));
   {
     base::RunLoop run_loop;
@@ -217,7 +223,7 @@
       example_request.shared_info().Clone();
   expected_shared_info.scheduled_report_time = kExampleTime;
 
-  scheduler_.ScheduleRequest(
+  scheduler_->ScheduleRequest(
       AggregatableReportRequest::Create(example_request.payload_contents(),
                                         std::move(expected_shared_info))
           .value());
@@ -245,7 +251,7 @@
   }
 
   // Request IDs are incremented from 1.
-  scheduler_.NotifyInProgressRequestFailed(
+  scheduler_->NotifyInProgressRequestFailed(
       AggregationServiceStorage::RequestId(1));
   {
     base::RunLoop run_loop;
@@ -306,7 +312,7 @@
         example_request.shared_info().Clone();
     expected_shared_info.scheduled_report_time = scheduled_report_time;
 
-    scheduler_.ScheduleRequest(
+    scheduler_->ScheduleRequest(
         AggregatableReportRequest::Create(example_request.payload_contents(),
                                           std::move(expected_shared_info))
             .value());
@@ -382,7 +388,7 @@
         example_request.shared_info().Clone();
     expected_shared_info.scheduled_report_time = scheduled_report_time;
 
-    scheduler_.ScheduleRequest(
+    scheduler_->ScheduleRequest(
         AggregatableReportRequest::Create(example_request.payload_contents(),
                                           std::move(expected_shared_info))
             .value());
@@ -416,7 +422,7 @@
       example_request.shared_info().Clone();
   expected_shared_info.scheduled_report_time = kExampleTime;
 
-  scheduler_.ScheduleRequest(
+  scheduler_->ScheduleRequest(
       AggregatableReportRequest::Create(example_request.payload_contents(),
                                         std::move(expected_shared_info))
           .value());
@@ -459,7 +465,7 @@
       example_request.shared_info().Clone();
   expected_shared_info.scheduled_report_time = kExampleTime;
 
-  scheduler_.ScheduleRequest(
+  scheduler_->ScheduleRequest(
       AggregatableReportRequest::Create(example_request.payload_contents(),
                                         std::move(expected_shared_info))
           .value());
@@ -505,7 +511,7 @@
         example_request.shared_info().Clone();
     expected_shared_info.scheduled_report_time = kExampleTime;
 
-    scheduler_.ScheduleRequest(
+    scheduler_->ScheduleRequest(
         AggregatableReportRequest::Create(example_request.payload_contents(),
                                           std::move(expected_shared_info))
             .value());
@@ -519,4 +525,55 @@
   task_environment_.FastForwardBy(kExampleTime - base::Time::Now());
 }
 
+class AggregatableReportSchedulerDebugModeTest
+    : public AggregatableReportSchedulerTest {
+ public:
+  AggregatableReportSchedulerDebugModeTest() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kPrivateAggregationDebugMode);
+  }
+};
+
+TEST_F(AggregatableReportSchedulerDebugModeTest,
+       NetworkOffline_ReportsAreSentImmediatelyWhenOnline) {
+  network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_NONE);  // Offline
+
+  AggregatableReportRequest example_request =
+      aggregation_service::CreateExampleRequest();
+
+  AggregatableReportSharedInfo expected_shared_info =
+      example_request.shared_info().Clone();
+  expected_shared_info.scheduled_report_time = kExampleTime;
+
+  scheduler_->ScheduleRequest(
+      AggregatableReportRequest::Create(example_request.payload_contents(),
+                                        std::move(expected_shared_info))
+          .value());
+
+  base::TimeDelta fast_forward_required = kExampleTime - base::Time::Now();
+
+  Checkpoint checkpoint;
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_callback_, Run).Times(0);
+    EXPECT_CALL(checkpoint, Call(1));
+    EXPECT_CALL(mock_callback_, Run);
+  }
+
+  // Need to fast forward beyond the report time so that it's in the past and
+  // will be updated.
+  task_environment_.FastForwardBy(fast_forward_required +
+                                  base::Microseconds(1));
+
+  checkpoint.Call(1);
+
+  network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_UNKNOWN);  // Online
+
+  // With the debug mode flag, the report should not be delayed, so all we need
+  // to do is run any pending tasks.
+  task_environment_.RunUntilIdle();
+}
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc
index 933d765..9d7aa8d 100644
--- a/content/browser/attribution_reporting/attributions_browsertest.cc
+++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -18,6 +18,9 @@
 #include "content/browser/attribution_reporting/attribution_manager_impl.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
 #include "content/browser/attribution_reporting/storable_source.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_core_observer.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/network_service_instance.h"
@@ -43,6 +46,9 @@
 #include "services/network/test/test_network_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -54,6 +60,50 @@
 
 constexpr char kBaseDataDir[] = "content/test/data/";
 
+void ExpectRegisterResultAndRun(blink::ServiceWorkerStatusCode expected,
+                                base::RepeatingClosure continuation,
+                                blink::ServiceWorkerStatusCode actual) {
+  EXPECT_EQ(expected, actual);
+  continuation.Run();
+}
+
+// Observer which waits for a service worker to register in the browser process
+// by observing worker activation status.
+class WorkerStateObserver : public ServiceWorkerContextCoreObserver {
+ public:
+  WorkerStateObserver(scoped_refptr<ServiceWorkerContextWrapper> context,
+                      ServiceWorkerVersion::Status target)
+      : context_(std::move(context)), target_(target) {
+    observation_.Observe(context_.get());
+  }
+
+  WorkerStateObserver(const WorkerStateObserver&) = delete;
+  WorkerStateObserver& operator=(const WorkerStateObserver&) = delete;
+
+  ~WorkerStateObserver() override = default;
+
+  // ServiceWorkerContextCoreObserver overrides.
+  void OnVersionStateChanged(int64_t version_id,
+                             const GURL& scope,
+                             const blink::StorageKey& key,
+                             ServiceWorkerVersion::Status) override {
+    const ServiceWorkerVersion* version = context_->GetLiveVersion(version_id);
+    if (version->status() == target_) {
+      context_->RemoveObserver(this);
+      run_loop_.Quit();
+    }
+  }
+  void Wait() { run_loop_.Run(); }
+
+ private:
+  base::RunLoop run_loop_;
+  scoped_refptr<ServiceWorkerContextWrapper> context_;
+  const ServiceWorkerVersion::Status target_;
+  base::ScopedObservation<ServiceWorkerContextWrapper,
+                          ServiceWorkerContextCoreObserver>
+      observation_{this};
+};
+
 // Waits for the a given |report_url| to be received by the test server. Wraps a
 // ControllableHttpResponse so that it can wait for the server request in a
 // thread-safe manner. Therefore, these must be registered prior to |server|
@@ -181,6 +231,13 @@
     https_server_->ServeFilesFromSourceDirectory("content/test/data");
     https_server_->ServeFilesFromSourceDirectory(
         "content/test/data/attribution_reporting");
+
+    StoragePartition* partition = shell()
+                                      ->web_contents()
+                                      ->GetBrowserContext()
+                                      ->GetDefaultStoragePartition();
+    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
+        partition->GetServiceWorkerContext());
   }
 
   void TearDownOnMainThread() override {
@@ -285,11 +342,16 @@
     return popup_contents;
   }
 
+  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
+  ServiceWorkerContext* public_context() { return wrapper(); }
+
  private:
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
 
   std::unique_ptr<network::TestNetworkConnectionTracker>
       network_connection_tracker_;
+
+  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
 };
 
 // Verifies that storage initialization does not hang when initialized in a
@@ -905,6 +967,164 @@
   expected_report.WaitForReport();
 }
 
+IN_PROC_BROWSER_TEST_F(
+    AttributionsBrowserTest,
+    ServiceWorkerPerformsAttributionSrcRedirect_ReporterSet) {
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server(), "/attribution_reporting/register_source_redirect");
+
+  ExpectedReportWaiter expected_report(
+      GURL("https://c.test/.well-known/attribution-reporting/"
+           "report-event-attribution"),
+      /*attribution_destination=*/"https://d.test",
+      /*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
+      https_server());
+  ASSERT_TRUE(https_server()->Start());
+
+  GURL impression_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/page_with_impression_creator.html");
+
+  // Setup our service worker.
+  WorkerStateObserver sw_observer(wrapper(), ServiceWorkerVersion::ACTIVATED);
+  blink::mojom::ServiceWorkerRegistrationOptions options(
+      impression_url, blink::mojom::ScriptType::kClassic,
+      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
+  blink::StorageKey key(url::Origin::Create(options.scope));
+  public_context()->RegisterServiceWorker(
+      https_server()->GetURL("a.test",
+                             "/attribution_reporting/service_worker.js"),
+      key, options,
+      base::BindOnce(&ExpectRegisterResultAndRun,
+                     blink::ServiceWorkerStatusCode::kOk, base::DoNothing()));
+  sw_observer.Wait();
+
+  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));
+
+  MockAttributionObserver observer;
+  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
+      &observer);
+  observation.Observe(attribution_manager());
+
+  base::RunLoop loop;
+  EXPECT_CALL(observer, OnSourceHandled(_, StorableSource::Result::kSuccess))
+      .WillOnce([&]() { loop.Quit(); });
+
+  EXPECT_TRUE(ExecJs(
+      web_contents(),
+      JsReplace(
+          "createAttributionSrcImg($1);",
+          https_server()->GetURL(
+              "a.test", "/attribution_reporting/register_source_redirect"))));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader(
+      "Location",
+      https_server()
+          ->GetURL("c.test",
+                   "/attribution_reporting/register_source_headers.html")
+          .spec());
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  // Wait until the source has been stored before registering the trigger;
+  // otherwise the trigger could be processed before the source, in which case
+  // there would be no matching source: crbug.com/1309173.
+  loop.Run();
+
+  GURL conversion_url = https_server()->GetURL(
+      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
+
+  GURL register_trigger_url = https_server()->GetURL(
+      "c.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
+
+  expected_report.WaitForReport();
+}
+
+IN_PROC_BROWSER_TEST_F(
+    AttributionsBrowserTest,
+    ServiceWorkerPerformsAttributionEligibleRedirect_ReporterSet) {
+  auto register_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          https_server(), "/attribution_reporting/register_source_redirect");
+
+  ExpectedReportWaiter expected_report(
+      GURL("https://c.test/.well-known/attribution-reporting/"
+           "report-event-attribution"),
+      /*attribution_destination=*/"https://d.test",
+      /*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
+      https_server());
+  ASSERT_TRUE(https_server()->Start());
+
+  GURL impression_url = https_server()->GetURL(
+      "a.test", "/attribution_reporting/page_with_impression_creator.html");
+
+  // Setup our service worker.
+  WorkerStateObserver sw_observer(wrapper(), ServiceWorkerVersion::ACTIVATED);
+  blink::mojom::ServiceWorkerRegistrationOptions options(
+      impression_url, blink::mojom::ScriptType::kClassic,
+      blink::mojom::ServiceWorkerUpdateViaCache::kImports);
+  blink::StorageKey key(url::Origin::Create(options.scope));
+  public_context()->RegisterServiceWorker(
+      https_server()->GetURL("a.test",
+                             "/attribution_reporting/service_worker.js"),
+      key, options,
+      base::BindOnce(&ExpectRegisterResultAndRun,
+                     blink::ServiceWorkerStatusCode::kOk, base::DoNothing()));
+  sw_observer.Wait();
+
+  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));
+
+  MockAttributionObserver observer;
+  base::ScopedObservation<AttributionManager, AttributionObserver> observation(
+      &observer);
+  observation.Observe(attribution_manager());
+
+  base::RunLoop loop;
+  EXPECT_CALL(observer, OnSourceHandled(_, StorableSource::Result::kSuccess))
+      .WillOnce([&]() { loop.Quit(); });
+
+  EXPECT_TRUE(ExecJs(
+      web_contents(),
+      JsReplace(
+          "createAttributionEligibleImgSrc($1);",
+          https_server()->GetURL(
+              "a.test", "/attribution_reporting/register_source_redirect"))));
+
+  register_response->WaitForRequest();
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+  http_response->AddCustomHeader(
+      "Location",
+      https_server()
+          ->GetURL("c.test",
+                   "/attribution_reporting/register_source_headers.html")
+          .spec());
+  register_response->Send(http_response->ToResponseString());
+  register_response->Done();
+
+  // Wait until the source has been stored before registering the trigger;
+  // otherwise the trigger could be processed before the source, in which case
+  // there would be no matching source: crbug.com/1309173.
+  loop.Run();
+
+  GURL conversion_url = https_server()->GetURL(
+      "d.test", "/attribution_reporting/page_with_conversion_redirect.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
+
+  GURL register_trigger_url = https_server()->GetURL(
+      "c.test", "/attribution_reporting/register_trigger_headers.html");
+  EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
+                                               register_trigger_url)));
+
+  expected_report.WaitForReport();
+}
+
 IN_PROC_BROWSER_TEST_F(AttributionsBrowserTest,
                        EventSourceWithDebugKeyConversion_ReportSent) {
   // Expected reports must be registered before the server starts.
diff --git a/content/browser/private_aggregation/private_aggregation_host.cc b/content/browser/private_aggregation/private_aggregation_host.cc
index 42f2542..136049a 100644
--- a/content/browser/private_aggregation/private_aggregation_host.cc
+++ b/content/browser/private_aggregation/private_aggregation_host.cc
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/check.h"
+#include "base/command_line.h"
 #include "base/guid.h"
 #include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
@@ -21,6 +22,7 @@
 #include "content/common/private_aggregation_host.mojom.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -53,7 +55,10 @@
                                  PrivateAggregationBudgetKey)>
         on_report_request_received,
     BrowserContext* browser_context)
-    : on_report_request_received_(std::move(on_report_request_received)),
+    : should_not_delay_reports_(
+          base::CommandLine::ForCurrentProcess()->HasSwitch(
+              switches::kPrivateAggregationDebugMode)),
+      on_report_request_received_(std::move(on_report_request_received)),
       browser_context_(*browser_context) {
   DCHECK(!on_report_request_received_.is_null());
 }
@@ -127,8 +132,10 @@
   base::Time now = base::Time::Now();
 
   AggregatableReportSharedInfo shared_info(
-      /*scheduled_report_time=*/GetScheduledReportTime(
-          /*report_issued_time=*/now),
+      /*scheduled_report_time=*/should_not_delay_reports_
+          ? now
+          : GetScheduledReportTime(
+                /*report_issued_time=*/now),
       /*report_id=*/base::GUID::GenerateRandomV4(), reporting_origin,
       debug_mode_details->is_enabled
           ? AggregatableReportSharedInfo::DebugMode::kEnabled
diff --git a/content/browser/private_aggregation/private_aggregation_host.h b/content/browser/private_aggregation/private_aggregation_host.h
index da2e533..7cc01dd 100644
--- a/content/browser/private_aggregation/private_aggregation_host.h
+++ b/content/browser/private_aggregation/private_aggregation_host.h
@@ -72,6 +72,9 @@
  private:
   struct ReceiverContext;
 
+  // Set iff the private aggregation debug mode is set.
+  bool should_not_delay_reports_;
+
   base::RepeatingCallback<void(AggregatableReportRequest,
                                PrivateAggregationBudgetKey)>
       on_report_request_received_;
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
index 03d5b5ac8f..ad2570a 100644
--- a/content/browser/private_aggregation/private_aggregation_host_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/guid.h"
 #include "base/test/gmock_move_support.h"
 #include "base/test/mock_callback.h"
@@ -21,6 +22,7 @@
 #include "content/browser/private_aggregation/private_aggregation_test_utils.h"
 #include "content/common/aggregatable_report.mojom.h"
 #include "content/common/private_aggregation_host.mojom.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_utils.h"
@@ -64,8 +66,6 @@
   TestBrowserContext test_browser_context_;
 };
 
-}  // namespace
-
 TEST_F(PrivateAggregationHostTest,
        SendHistogramReport_ReportRequestHasCorrectMembers) {
   const url::Origin kExampleOrigin =
@@ -448,4 +448,49 @@
   EXPECT_TRUE(remote.is_connected());
 }
 
+class PrivateAggregationHostDebugModeTest : public PrivateAggregationHostTest {
+ public:
+  PrivateAggregationHostDebugModeTest() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kPrivateAggregationDebugMode);
+  }
+};
+
+TEST_F(PrivateAggregationHostDebugModeTest,
+       SendHistogramReport_ScheduledReportTimeIsNotDelayed) {
+  const url::Origin kExampleOrigin =
+      url::Origin::Create(GURL("https://example.com"));
+  const url::Origin kMainFrameOrigin =
+      url::Origin::Create(GURL("https://main_frame.com"));
+
+  mojo::Remote<mojom::PrivateAggregationHost> remote;
+  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
+                                     PrivateAggregationBudgetKey::Api::kFledge,
+                                     remote.BindNewPipeAndPassReceiver()));
+
+  absl::optional<AggregatableReportRequest> validated_request;
+  EXPECT_CALL(mock_callback_,
+              Run(_, Property(&PrivateAggregationBudgetKey::api,
+                              PrivateAggregationBudgetKey::Api::kFledge)))
+      .WillOnce(MoveArg<0>(&validated_request));
+
+  std::vector<mojom::AggregatableReportHistogramContributionPtr> contributions;
+  contributions.push_back(mojom::AggregatableReportHistogramContribution::New(
+      /*bucket=*/123, /*value=*/456));
+  remote->SendHistogramReport(std::move(contributions),
+                              mojom::AggregationServiceMode::kDefault,
+                              mojom::DebugModeDetails::New());
+
+  remote.FlushForTesting();
+  EXPECT_TRUE(remote.is_connected());
+
+  ASSERT_TRUE(validated_request);
+
+  // We're using `MOCK_TIME` so we can be sure no time has advanced.
+  EXPECT_EQ(validated_request->shared_info().scheduled_report_time,
+            base::Time::Now());
+}
+
+}  // namespace
+
 }  // namespace content
diff --git a/content/browser/web_package/subresource_loading_origin_trial.md b/content/browser/web_package/subresource_loading_origin_trial.md
deleted file mode 100644
index 7ad5dd7..0000000
--- a/content/browser/web_package/subresource_loading_origin_trial.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Origin Trial for Subresource Loading with Web Bundles
-
-This document is for web developers who want to participate in Origin Trial for
-[Subresource Loading with Web Bundles][explainer].
-
-- [Chrome Status]
-- [Explainer]
-- [Intent to Experiment](https://groups.google.com/a/chromium.org/g/blink-dev/c/9CwkzaF_eQ4/m/kuR07FTTCAAJ)
-- [Registration Form](https://developer.chrome.com/origintrials/#/view_trial/-6307291278132379647)
-
-## Origin Trial timeline
-
-- Chrome M90-M101, M103-M104
-
-  Note: M102 is excluded.
-
-## How to create a bundle
-
-There are several tools available.
-
-- Go: [gen-bundle](https://github.com/WICG/webpackage/tree/master/go/bundle)
-  tool in the WICG/webpackage repository.
-- npm: [wbn](https://www.npmjs.com/package/wbn)
-
-## What works in Chrome M97+
-
-### `<script>`-based API
-
-Example:
-
-```html
-<script type="webbundle">
-{
-  "source": "https://example.com/dir/subresources.wbn",
-  "credentials": "include",
-  "resources": ["a.js", "b.js", "c.png"],
-  "scopes": ["css"]
-}
-</script>
-```
-
-### `uuid-in-package` URL
-
-Example:
-
-```html
-<script type="webbundle">
-{
-  "source": "https://example.com/dir/subresources.wbn",
-  "resources": ["uuid-in-package:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"]
-}
-</script>
-
-<iframe src="uuid-in-package:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"></iframe>
-```
-
-### Web Bundles format version "b2"
-
-Chrome M97+ supports
-[the latest Web Bundles format](https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html)
-(called as "b2").
-
-## The old APIs
-
-Chrome M90 - M101 supported `<link>`-based API, a `urn:uuid` URL and the old
-WebBundle format
-["b1"](https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html).
-however, they were removed in M102.
-
-This guide no longer covers the old APIs. If you are still using the old APIs,
-Please see
-[the previous revision of this guide](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/web_package/subresource_loading_origin_trial.md;drc=1454cf984a485a136c4a525ab79f6cf0a3877504)
-for the old APIs and [the migration guide](https://docs.google.com/document/d/1hAl7jb-a9WET_mSeHBD9HxIBUwUe65Dbyn6u6LRB61s/edit?usp=sharing).
-
-## Feature detection
-
-You can use
-[`HTMLScriptElement.supports(type)`](https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports)
-for feature detection.
-
-```js
-if (HTMLScriptElement.supports("webbundle")) {
-   // Supported
-   ...
-} else {
-   // Unsupported
-   ...
-}
-```
-
-# How to try this feature locally
-
-Enable _Experimental Web Platform Features_ flag
-([chrome://flags/#enable-experimental-web-platform-features](chrome://flags/#enable-experimental-web-platform-features)).
-Note that an earlier version of Chrome might not support this feature.
-
-[chrome status]: https://www.chromestatus.com/feature/5710618575241216
-[explainer]: https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md
diff --git a/content/browser/web_package/using_web_bundles.md b/content/browser/web_package/using_web_bundles.md
index ad4391d..fafd3f3 100644
--- a/content/browser/web_package/using_web_bundles.md
+++ b/content/browser/web_package/using_web_bundles.md
@@ -2,7 +2,7 @@
 
 This document is for web developers who want to create [Web Bundles](https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html) that can be loaded with Chromium's experimental implementation.
 
-*NOTE*: This document describes about Web Bundles that contain entire web page(s). For [Subresource Loading with Web Bundles](https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md), see [subresource_loading_origin_trial.md](subresource_loading_origin_trial.md).
+*NOTE*: This document describes about Web Bundles that contain entire web page(s). For [Subresource Loading with Web Bundles](https://chromestatus.com/feature/5710618575241216), see [the explainer](https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md).
 
 [TOC]
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java b/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
index adf6a9a..cbf69c1 100644
--- a/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/font/AndroidFontLookupImpl.java
@@ -11,7 +11,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.provider.FontRequest;
@@ -55,10 +54,6 @@
     private static final String READ_ONLY_MODE = "r";
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    static final String FETCH_FONT_NAME_HISTOGRAM = "Android.FontLookup.FetchFontName";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    static final String FETCH_FONT_RESULT_HISTOGRAM = "Android.FontLookup.FetchFontResult";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     static final String MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM =
             "Android.FontLookup.MatchLocalFontByUniqueName.Time";
     static final String FETCH_ALL_FONT_FILES_HISTOGRAM =
@@ -109,41 +104,6 @@
         mExpectedFonts = new HashSet<>(mFullFontNameToQuery.keySet());
     }
 
-    // These values are persisted to logs. Entries should not be renumbered and
-    // numeric values should never be reused. These values must stay in sync with enums.xml.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    @IntDef({FetchFontName.GOOGLE_SANS_REGULAR, FetchFontName.GOOGLE_SANS_MEDIUM,
-            FetchFontName.GOOGLE_SANS_BOLD, FetchFontName.NOTO_COLOR_EMOJI_COMPAT})
-    @interface FetchFontName {
-        int OTHER = 0;
-        int GOOGLE_SANS_REGULAR = 1;
-        int GOOGLE_SANS_MEDIUM = 2;
-        int GOOGLE_SANS_BOLD = 3;
-        int NOTO_COLOR_EMOJI_COMPAT = 4;
-        int COUNT = 5;
-    }
-
-    // These values are persisted to logs. Entries should not be renumbered and
-    // numeric values should never be reused. These values must stay in sync with enums.xml.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    @IntDef({FetchFontResult.SUCCESS, FetchFontResult.FAILED_UNEXPECTED_NAME,
-            FetchFontResult.FAILED_STATUS_CODE, FetchFontResult.FAILED_NON_UNIQUE_RESULT,
-            FetchFontResult.FAILED_RESULT_CODE, FetchFontResult.FAILED_FILE_OPEN,
-            FetchFontResult.FAILED_EXCEPTION, FetchFontResult.FAILED_AVOID_RETRY,
-            FetchFontResult.SUCCESS_CACHED})
-    @interface FetchFontResult {
-        int SUCCESS = 0;
-        int FAILED_UNEXPECTED_NAME = 1;
-        int FAILED_STATUS_CODE = 2;
-        int FAILED_NON_UNIQUE_RESULT = 3;
-        int FAILED_RESULT_CODE = 4;
-        int FAILED_FILE_OPEN = 5;
-        int FAILED_EXCEPTION = 6;
-        int FAILED_AVOID_RETRY = 7;
-        int SUCCESS_CACHED = 8;
-        int COUNT = 9;
-    }
-
     /**
      * Synchronously returns the list of fonts (by ICU case folded full font name) that may be
      * available from GMS Core on-device. These fonts should have already been preloaded via the
@@ -176,8 +136,6 @@
             @NonNull String fontUniqueName, MatchLocalFontByUniqueName_Response callback) {
         long startTimeMs = SystemClock.elapsedRealtime();
 
-        logFetchFontName(fontUniqueName);
-
         // Get executor associated with the current thread for running Mojo callback.
         Core core = CoreImpl.getInstance();
         Executor executor = ExecutorFactory.getExecutorForCurrentThread(core);
@@ -249,7 +207,6 @@
         ParcelFileDescriptor cachedFd = mFetchedFontCache.get(fontUniqueName);
         if (cachedFd != null) {
             try {
-                logFetchFontResult(FetchFontResult.SUCCESS_CACHED);
                 return cachedFd.dup();
             } catch (IOException e) {
                 StreamUtil.closeQuietly(cachedFd);
@@ -260,13 +217,11 @@
         String query = mFullFontNameToQuery.get(fontUniqueName);
         if (query == null) {
             Log.d(TAG, "Query format not found for full font name: %s", fontUniqueName);
-            logFetchFontResult(FetchFontResult.FAILED_UNEXPECTED_NAME);
             return null;
         }
 
         if (!mExpectedFonts.contains(fontUniqueName)) {
             Log.d(TAG, "Skipping fetch for font that previously failed: %s", fontUniqueName);
-            logFetchFontResult(FetchFontResult.FAILED_AVOID_RETRY);
             return null;
         }
 
@@ -283,7 +238,6 @@
             if (fontFamilyResult.getStatusCode() != FontFamilyResult.STATUS_OK) {
                 Log.d(TAG, "Font fetch failed with status code: %d",
                         fontFamilyResult.getStatusCode());
-                logFetchFontResult(FetchFontResult.FAILED_STATUS_CODE);
                 return null;
             }
 
@@ -291,14 +245,12 @@
             if (fontInfos.length != 1) {
                 Log.d(TAG, "Font fetch did not return a unique result: length = %d",
                         fontInfos.length);
-                logFetchFontResult(FetchFontResult.FAILED_NON_UNIQUE_RESULT);
                 return null;
             }
 
             FontInfo fontInfo = fontInfos[0];
             if (fontInfo.getResultCode() != FontsContractCompat.Columns.RESULT_CODE_OK) {
                 Log.d(TAG, "Returned font has failed status code: %d", fontInfo.getResultCode());
-                logFetchFontResult(FetchFontResult.FAILED_RESULT_CODE);
                 return null;
             }
 
@@ -307,11 +259,9 @@
                     contentResolver.openFileDescriptor(fontInfo.getUri(), READ_ONLY_MODE);
             if (fileDescriptor == null) {
                 Log.d(TAG, "Unable to open font file at: %s", fontInfo.getUri());
-                logFetchFontResult(FetchFontResult.FAILED_FILE_OPEN);
                 return null;
             }
 
-            logFetchFontResult(FetchFontResult.SUCCESS);
             if (ContentFeatureList.isEnabled(BlinkFeatures.PREFETCH_ANDROID_FONTS)) {
                 mFetchedFontCache.put(fontUniqueName, fileDescriptor.dup());
                 // The size of the font cache should be at maximum the size of the font name to
@@ -323,7 +273,6 @@
             // We sometimes get CursorWindowAllocationException, but it's a hidden class. So, we
             // catch RuntimeException.
             Log.d(TAG, "Failed to get font with: %s", e.toString());
-            logFetchFontResult(FetchFontResult.FAILED_EXCEPTION);
             return null;
         }
     }
@@ -338,7 +287,6 @@
      * 2. Keys should be ICU case folded full font name. This can be done manually with
      *    icu_fold_case_util.cc, or in Java by importing the ICU4J third_party library. (The
      *    CaseMap.Fold Java API is only available in Android API 29+.)
-     * 3. Update the {@link FetchFontName} enum entries.
      *
      * @return The created map from font names to queries.
      */
@@ -363,34 +311,6 @@
         return String.format(Locale.US, "name=%s&weight=%d&besteffort=false", name, weight);
     }
 
-    private static void logFetchFontResult(@FetchFontResult int result) {
-        RecordHistogram.recordEnumeratedHistogram(
-                FETCH_FONT_RESULT_HISTOGRAM, result, FetchFontResult.COUNT);
-    }
-
-    private static void logFetchFontName(String fontName) {
-        @FetchFontName
-        int result;
-        switch (fontName) {
-            case GOOGLE_SANS_REGULAR:
-                result = FetchFontName.GOOGLE_SANS_REGULAR;
-                break;
-            case GOOGLE_SANS_MEDIUM:
-                result = FetchFontName.GOOGLE_SANS_MEDIUM;
-                break;
-            case GOOGLE_SANS_BOLD:
-                result = FetchFontName.GOOGLE_SANS_BOLD;
-                break;
-            case NOTO_COLOR_EMOJI_COMPAT:
-                result = FetchFontName.NOTO_COLOR_EMOJI_COMPAT;
-                break;
-            default:
-                result = FetchFontName.OTHER;
-        }
-        RecordHistogram.recordEnumeratedHistogram(
-                FETCH_FONT_NAME_HISTOGRAM, result, FetchFontName.COUNT);
-    }
-
     @Override
     public void close() {}
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java b/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
index 324d3771..3dc5518 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/font/AndroidFontLookupImplTest.java
@@ -53,8 +53,7 @@
 import org.chromium.blink.mojom.AndroidFontLookup.FetchAllFontFiles_Response;
 import org.chromium.blink.mojom.AndroidFontLookup.GetUniqueNameLookupTable_Response;
 import org.chromium.blink.mojom.AndroidFontLookup.MatchLocalFontByUniqueName_Response;
-import org.chromium.content.browser.font.AndroidFontLookupImpl.FetchFontName;
-import org.chromium.content.browser.font.AndroidFontLookupImpl.FetchFontResult;
+;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.mojo.MojoTestRule;
 import org.chromium.mojo_base.mojom.ReadOnlyFile;
@@ -259,15 +258,6 @@
                 .call(isNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_UNEXPECTED_NAME));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -288,15 +278,6 @@
                 .call(isNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_STATUS_CODE));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -316,15 +297,6 @@
                 .call(isNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_NON_UNIQUE_RESULT));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -346,15 +318,6 @@
                 .call(isNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_RESULT_CODE));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -373,15 +336,6 @@
                 .call(isNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_EXCEPTION));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -400,11 +354,6 @@
                 timeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL))
                 .call(isNull());
 
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_EXCEPTION));
-
         // Second request should early out with FAILED_AVOID_RETRY.
         mAndroidFontLookup.matchLocalFontByUniqueName(
                 FULL_FONT_NAME_1, mMatchLocalFontByUniqueNameCallback);
@@ -415,15 +364,6 @@
                 .call(isNull());
 
         assertEquals(2,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.FAILED_AVOID_RETRY));
-
-        assertEquals(2,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
@@ -445,15 +385,6 @@
                 .call(notNull());
 
         assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_NAME_HISTOGRAM, FetchFontName.OTHER));
-
-        assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        AndroidFontLookupImpl.FETCH_FONT_RESULT_HISTOGRAM,
-                        FetchFontResult.SUCCESS));
-
-        assertEquals(1,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         AndroidFontLookupImpl.MATCH_LOCAL_FONT_BY_UNIQUE_NAME_HISTOGRAM));
     }
diff --git a/content/public/browser/preloading.h b/content/public/browser/preloading.h
index dd32f712..176894d4 100644
--- a/content/public/browser/preloading.h
+++ b/content/public/browser/preloading.h
@@ -200,7 +200,16 @@
   // destroyed" for prerender, but "the user already had cookies for a
   // cross-origin prefetch"
   // for prefetch).
+  //
+  // Values between kPreloadingFailureReasonCommonEnd (included) and
+  // kPreloadingFailureReasonContentEnd (excluded) are reserved for enums
+  // defined in //content.
   kPreloadingFailureReasonCommonEnd = 100,
+
+  // Values beyond this value are for failure reasons defined by the embedder.
+  // The semantics of those values can vary by preloading type (1000 can mean
+  // "limit exceeded" for preconnect but "cancelled" for prerender).
+  kPreloadingFailureReasonContentEnd = 1000,
 };
 
 }  // namespace content
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 12a150e..5bc3b07a5 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -598,6 +598,9 @@
 // --no-sandbox as well or the sandbox won't allow the dialog to display.
 const char kPpapiStartupDialog[]            = "ppapi-startup-dialog";
 
+// Causes the Private Aggregation API to run without delays.
+const char kPrivateAggregationDebugMode[] = "private-aggregation-debug-mode";
+
 // Enable the "Process Per Site" process model for all domains. This mode
 // consolidates same-site pages so that they share a single process.
 //
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 8e3fa53..eead57aa 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -180,6 +180,7 @@
 extern const char kPpapiPluginLauncher[];
 CONTENT_EXPORT extern const char kPpapiPluginProcess[];
 extern const char kPpapiStartupDialog[];
+CONTENT_EXPORT extern const char kPrivateAggregationDebugMode[];
 CONTENT_EXPORT extern const char kProcessPerSite[];
 CONTENT_EXPORT extern const char kProcessPerTab[];
 CONTENT_EXPORT extern const char kProcessType[];
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 029db300..44858e4f 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -288,8 +288,7 @@
 
   const WebDocument& document = GetMainDocument();
   if (!document.IsNull()) {
-    auto root_obj = WebAXObject::FromWebDocument(document);
-    if (root_obj.MaybeUpdateLayoutAndCheckValidity()) {
+    if (WebAXObject::MaybeUpdateLayoutAndCheckValidity(document)) {
       // 1. Now that layout has been updated for the entire document, try to run
       // the hit test operation on the popup root element, if there's a popup
       // opened. This is needed to allow hit testing within web content popups.
@@ -305,8 +304,10 @@
       // 2. If running the hit test operation on the popup didn't returned any
       // result (or if there was no popup), run the hit test operation from the
       // main element.
-      if (ax_object.IsNull())
+      if (ax_object.IsNull()) {
+        auto root_obj = WebAXObject::FromWebDocument(document);
         ax_object = root_obj.HitTest(point);
+      }
     }
   }
 
@@ -376,8 +377,7 @@
   if (document.IsNull())
     return;
 
-  auto root = WebAXObject::FromWebDocument(document);
-  if (!root.MaybeUpdateLayoutAndCheckValidity())
+  if (!WebAXObject::MaybeUpdateLayoutAndCheckValidity(document))
     return;
 
   // If an action was requested, we no longer want to defer events.
@@ -396,14 +396,14 @@
   if (target->PerformAction(data))
     return;
 
-  if (!root.MaybeUpdateLayoutAndCheckValidity())
+  if (!WebAXObject::MaybeUpdateLayoutAndCheckValidity(document))
     return;
 
   switch (data.action) {
     case ax::mojom::Action::kBlur: {
       ui::AXActionData action_data;
       action_data.action = ax::mojom::Action::kFocus;
-      root.PerformAction(action_data);
+      ComputeRoot().PerformAction(action_data);
       break;
     }
     case ax::mojom::Action::kGetImageData:
@@ -467,10 +467,12 @@
       }
       break;
     case ax::mojom::Action::kSignalEndOfTest:
-      // Wait for 100ms to allow pending events to come in
+      // Wait for 100ms to allow pending events to come in.
+      // TODO(accessibility) Remove sleep() hack; it should no longer be needed.
       base::PlatformThread::Sleep(base::Milliseconds(100));
 
-      HandleAXEvent(ui::AXEvent(root.AxID(), ax::mojom::Event::kEndOfTest));
+      HandleAXEvent(
+          ui::AXEvent(ComputeRoot().AxID(), ax::mojom::Event::kEndOfTest));
       break;
     case ax::mojom::Action::kShowTooltip:
     case ax::mojom::Action::kHideTooltip:
@@ -1080,19 +1082,15 @@
     dirty_objects_.pop_front();
     auto obj = current_dirty_object->obj;
 
+    // Cannot serialize detached or unincluded object.
     // Dirty objects can be added using MarkWebAXObjectDirty(obj) from other
-    // parts of the code as well, so we need to ensure the object still exists.
-    // TODO(accessibility) Change this to CheckValidity() if there aren't crash
-    // reports of illegal lifecycle changes from WebDisallowTransitionScope.
-    if (obj.IsDetached() || !obj.MaybeUpdateLayoutAndCheckValidity())
-      continue;
-
-    // Cannot serialize unincluded object.
-    // Only included objects are marked dirty, but this can happen if the object
-    // becomes unincluded after it was originally marked dirty, in which case a
-    // children changed will also be fired on the included ancestor. The
-    // children changed event on the ancestor means that attempting to serialize
-    // this unincluded object is not necessary.
+    // parts of the code as well, so we need to ensure the object still exists
+    // and is still included in the tree. Only included objects are marked
+    // dirty, but this can happen if the object becomes unincluded after it was
+    // originally marked dirty, in which case a children changed will also be
+    // fired on the included ancestor. The children changed event on the
+    // ancestor means that attempting to serialize this unincluded object is not
+    // necessary.
     if (!obj.AccessibilityIsIncludedInTree())
       continue;
 
@@ -1273,17 +1271,7 @@
   // We ensured layout validity for the main document in the loop above; if a
   // popup is open, do the same for it.
   WebDocument popup_document = GetPopupDocument();
-  if (!popup_document.IsNull()) {
-    WebAXObject popup_root_obj = WebAXObject::FromWebDocument(popup_document);
-    if (!popup_root_obj.IsNull() &&
-        !popup_root_obj.MaybeUpdateLayoutAndCheckValidity()) {
-      // If a popup is open but we can't ensure its validity, return without
-      // sending an update bundle, the same as we would for a node in the main
-      // document.
-      // Do not perform this check unless the popup has an a11y tree.
-      return;
-    }
-  }
+  WebAXObject::UpdateLayout(popup_document);
 
 #if DCHECK_IS_ON()
   // Protect against lifecycle changes in the popup document, if any.
@@ -1366,13 +1354,6 @@
 
 void RenderAccessibilityImpl::SendLocationChanges() {
   TRACE_EVENT0("accessibility", "RenderAccessibilityImpl::SendLocationChanges");
-  // Update layout on the root of the tree.
-
-  // TODO(accessibility) Change this to CheckValidity() if there aren't crash
-  // reports of illegal lifecycle changes from WebDisallowTransitionScope.
-  if (!ComputeRoot().MaybeUpdateLayoutAndCheckValidity())
-    return;
-
   ax_context_->SerializeLocationChanges();
 }
 
@@ -1602,14 +1583,9 @@
 }
 
 WebAXObject RenderAccessibilityImpl::ComputeRoot() {
-  if (!render_frame_ || !render_frame_->GetWebFrame())
-    return WebAXObject();
-
-  WebDocument document = render_frame_->GetWebFrame()->GetDocument();
-  if (!document.IsNull())
-    return WebAXObject::FromWebDocument(document);
-
-  return WebAXObject();
+  DCHECK(render_frame_);
+  DCHECK(render_frame_->GetWebFrame());
+  return WebAXObject::FromWebDocument(GetMainDocument());
 }
 
 void RenderAccessibilityImpl::CancelScheduledEvents() {
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index babcb67..24fb2593 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -647,6 +647,7 @@
   EXPECT_EQ(6, CountAccessibilityNodesSentToBrowser());
 
   WebDocument document = GetMainFrame()->GetDocument();
+  // Getting the root object will also force layout.
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   WebAXObject html = root_obj.ChildAt(0);
   WebAXObject body = html.ChildAt(0);
@@ -657,8 +658,7 @@
   // Hide node "B" ("C" stays visible).
   ExecuteJavaScriptForTests(
       "document.getElementById('B').style.visibility = 'hidden';");
-  // Force layout now.
-  root_obj.MaybeUpdateLayoutAndCheckValidity();
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
 
   // Send a childrenChanged on "A".
   ClearHandledUpdates();
@@ -697,6 +697,7 @@
   EXPECT_EQ(6, CountAccessibilityNodesSentToBrowser());
 
   WebDocument document = GetMainFrame()->GetDocument();
+  // Getting the root object also forces a layout.
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   WebAXObject html = root_obj.ChildAt(0);
   WebAXObject body = html.ChildAt(0);
@@ -707,8 +708,8 @@
   // Show node "B", then send a childrenChanged on "A".
   ExecuteJavaScriptForTests(
       "document.getElementById('B').style.visibility = 'visible';");
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
 
-  root_obj.MaybeUpdateLayoutAndCheckValidity();
   ClearHandledUpdates();
 
   GetRenderAccessibilityImpl()->HandleAXEvent(
@@ -877,6 +878,7 @@
   LoadHTMLAndRefreshAccessibilityTree(html);
 
   WebDocument document = GetMainFrame()->GetDocument();
+  // Getting the root object also forces a layout.
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   WebAXObject html_elem = root_obj.ChildAt(0);
   WebAXObject body = html_elem.ChildAt(0);
@@ -890,10 +892,6 @@
   action.action = ax::mojom::Action::kFocus;
   GetRenderAccessibilityImpl()->PerformAction(action);
 
-  // Update layout so that the AXEvents themselves are queued up to
-  // RenderAccessibilityImpl.
-  ASSERT_TRUE(root_obj.MaybeUpdateLayoutAndCheckValidity());
-
   // Now perform the default action on the link, which will bounce focus to
   // the button element.
   action.target_node_id = link.AxID();
@@ -1170,8 +1168,8 @@
 #if !BUILDFLAG(IS_ANDROID)
   EXPECT_FALSE(IsSelected(option));
   EXPECT_TRUE(option_action_target->SetSelected(true));
-  // Seleting option requires layout to be clean.
-  ASSERT_TRUE(root_obj.MaybeUpdateLayoutAndCheckValidity());
+  // Selecting option requires layout to be clean.
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
   EXPECT_TRUE(IsSelected(option));
 #endif
 
@@ -1185,7 +1183,7 @@
   EXPECT_EQ(value_to_set, input_text.GetValueForControl().Utf8());
 
   // Setting selection requires layout to be clean.
-  ASSERT_TRUE(root_obj.MaybeUpdateLayoutAndCheckValidity());
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
 
   EXPECT_TRUE(text_one_action_target->SetSelection(
       text_one_action_target.get(), 3, text_two_action_target.get(), 4));
@@ -1295,8 +1293,8 @@
   // Show node "B".
   ExecuteJavaScriptForTests(
       "document.getElementById('B').style.visibility = 'visible';");
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
   ClearHandledUpdates();
-  root_obj.MaybeUpdateLayoutAndCheckValidity();
 
   // This should update the annotations of all images on the page, including the
   // already visible one.
@@ -1352,6 +1350,7 @@
 
   // Update node "A".
   ExecuteJavaScriptForTests("document.querySelector('img').src = 'test2.jpg';");
+  ASSERT_TRUE(WebAXObject::MaybeUpdateLayoutAndCheckValidity(document));
 
   ClearHandledUpdates();
   // This should update the annotations of all images on the page, including the
diff --git a/content/test/data/attribution_reporting/service_worker.js b/content/test/data/attribution_reporting/service_worker.js
new file mode 100644
index 0000000..557ac43
--- /dev/null
+++ b/content/test/data/attribution_reporting/service_worker.js
@@ -0,0 +1,5 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+self.addEventListener('fetch', e => { e.respondWith(fetch(e.request)) });
diff --git a/content/web_test/renderer/accessibility_controller.cc b/content/web_test/renderer/accessibility_controller.cc
index 8bcbe54..d7f543b 100644
--- a/content/web_test/renderer/accessibility_controller.cc
+++ b/content/web_test/renderer/accessibility_controller.cc
@@ -274,13 +274,11 @@
 
 v8::Local<v8::Object> AccessibilityController::AccessibleElementById(
     const std::string& id) {
-  blink::WebAXObject::UpdateLayout(
-      web_view()->MainFrame()->ToWebLocalFrame()->GetDocument());
+  const blink::WebDocument& web_document =
+      web_view()->MainFrame()->ToWebLocalFrame()->GetDocument();
+  blink::WebAXObject::UpdateLayout(web_document);
   blink::WebAXObject root_element = GetAccessibilityObjectForMainFrame();
 
-  if (!root_element.MaybeUpdateLayoutAndCheckValidity())
-    return v8::Local<v8::Object>();
-
   return FindAccessibleElementByIdRecursive(
       root_element, blink::WebString::FromUTF8(id.c_str()));
 }
diff --git a/content/web_test/renderer/web_ax_object_proxy.cc b/content/web_test/renderer/web_ax_object_proxy.cc
index 6af0330..7c3d049 100644
--- a/content/web_test/renderer/web_ax_object_proxy.cc
+++ b/content/web_test/renderer/web_ax_object_proxy.cc
@@ -1235,9 +1235,6 @@
   if (accessibility_object_.Role() != ax::mojom::Role::kStaticText)
     return std::string();
 
-  if (!accessibility_object_.MaybeUpdateLayoutAndCheckValidity())
-    return std::string();
-
   int len = end - start;
 
   // Get the bounds for each character and union them into one large rectangle.
diff --git a/extensions/browser/api/system_display/display_info_provider.cc b/extensions/browser/api/system_display/display_info_provider.cc
index 67050a5..ff7eeee 100644
--- a/extensions/browser/api/system_display/display_info_provider.cc
+++ b/extensions/browser/api/system_display/display_info_provider.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/extensions_browser_client.h"
@@ -112,20 +113,33 @@
 
 void DisplayInfoProvider::EnableUnifiedDesktop(bool enable) {}
 
-void DisplayInfoProvider::GetAllDisplaysInfo(
-    bool /* single_unified*/,
-    base::OnceCallback<void(DisplayUnitInfoList result)> callback) {
-  int64_t primary_id = screen_->GetPrimaryDisplay().id();
-  std::vector<display::Display> displays = screen_->GetAllDisplays();
+DisplayInfoProvider::DisplayUnitInfoList
+DisplayInfoProvider::GetAllDisplaysInfoList(
+    const std::vector<display::Display>& displays,
+    int64_t primary_id) const {
   DisplayUnitInfoList all_displays;
+
   for (const display::Display& display : displays) {
     api::system_display::DisplayUnitInfo unit =
         CreateDisplayUnitInfo(display, primary_id);
     UpdateDisplayUnitInfoForPlatform(display, &unit);
     all_displays.push_back(std::move(unit));
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), std::move(all_displays)));
+  return all_displays;
+}
+
+void DisplayInfoProvider::GetAllDisplaysInfo(
+    bool /* single_unified*/,
+    base::OnceCallback<void(DisplayUnitInfoList result)> callback) {
+  int64_t primary_id = screen_->GetPrimaryDisplay().id();
+  std::vector<display::Display> displays = screen_->GetAllDisplays();
+  DisplayUnitInfoList all_displays;
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      base::BindOnce(&DisplayInfoProvider::GetAllDisplaysInfoList,
+                     base::Unretained(this),  // `this` is a global singleton.
+                     displays, primary_id),
+      std::move(callback));
 }
 
 void DisplayInfoProvider::GetDisplayLayout(
@@ -206,7 +220,7 @@
 
 void DisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
     const display::Display& display,
-    extensions::api::system_display::DisplayUnitInfo* unit) {
+    extensions::api::system_display::DisplayUnitInfo* unit) const {
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
diff --git a/extensions/browser/api/system_display/display_info_provider.h b/extensions/browser/api/system_display/display_info_provider.h
index bfaa7003..9fc64fe 100644
--- a/extensions/browser/api/system_display/display_info_provider.h
+++ b/extensions/browser/api/system_display/display_info_provider.h
@@ -125,6 +125,12 @@
   // Trigger OnDisplayChangedEvent
   void DispatchOnDisplayChangedEvent();
 
+  // Convert a vector of Displays into a DisplayUnitInfoList. This function
+  // needs to be thread-safe since it is called via PostTask.
+  DisplayUnitInfoList GetAllDisplaysInfoList(
+      const std::vector<display::Display>& displays,
+      int64_t primary_id) const;
+
   // Create a DisplayUnitInfo from a display::Display for implementations of
   // GetAllDisplaysInfo()
   static api::system_display::DisplayUnitInfo CreateDisplayUnitInfo(
@@ -132,11 +138,11 @@
       int64_t primary_display_id);
 
  private:
-  // Update the content of the |unit| obtained for |display| using
-  // platform specific method.
+  // Update the content of the `unit` obtained for `display` using
+  // platform specific method. This must be safe to call off the ui thread.
   virtual void UpdateDisplayUnitInfoForPlatform(
       const display::Display& display,
-      api::system_display::DisplayUnitInfo* unit);
+      api::system_display::DisplayUnitInfo* unit) const;
 
   // DisplayObserver
   void OnDisplayAdded(const display::Display& new_display) override;
diff --git a/extensions/browser/api/system_display/system_display_apitest.cc b/extensions/browser/api/system_display/system_display_apitest.cc
index 741dd77..6771b0e9 100644
--- a/extensions/browser/api/system_display/system_display_apitest.cc
+++ b/extensions/browser/api/system_display/system_display_apitest.cc
@@ -357,4 +357,26 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_WIN)
+using SystemDisplayGetInfoTest = ShellApiTest;
+
+IN_PROC_BROWSER_TEST_F(SystemDisplayGetInfoTest, GetInfo) {
+  DisplayInfoProvider::ResetForTesting();
+  scoped_refptr<const Extension> test_extension =
+      ExtensionBuilder("Test", ExtensionBuilder::Type::PLATFORM_APP).Build();
+
+  scoped_refptr<SystemDisplayGetInfoFunction> get_info_function(
+      new SystemDisplayGetInfoFunction());
+
+  get_info_function->set_has_callback(true);
+  get_info_function->set_extension(test_extension.get());
+
+  // Verify that the GetInfo function runs successfully. This uses the real
+  // DisplayInfoProvider to verify that DisplayInfoProvider::GetAllDisplaysInfo
+  // calls its callback.
+  ASSERT_TRUE(api_test_utils::RunFunction(get_info_function.get(), "[]",
+                                          browser_context()));
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace extensions
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 7bee8cd3..ba9ad0f 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -3022,13 +3022,13 @@
     }
 
     if (auth_credentials_value) {
-      const base::DictionaryValue* credentials_value = nullptr;
-      EXTENSION_FUNCTION_VALIDATE(
-          auth_credentials_value->GetAsDictionary(&credentials_value));
+      const base::Value::Dict* credentials_value =
+          auth_credentials_value->GetIfDict();
+      EXTENSION_FUNCTION_VALIDATE(credentials_value);
       const std::string* username =
-          credentials_value->FindStringKey(keys::kUsernameKey);
+          credentials_value->FindString(keys::kUsernameKey);
       const std::string* password =
-          credentials_value->FindStringKey(keys::kPasswordKey);
+          credentials_value->FindString(keys::kPasswordKey);
       EXTENSION_FUNCTION_VALIDATE(username);
       EXTENSION_FUNCTION_VALIDATE(password);
       response->auth_credentials = net::AuthCredentials(
diff --git a/extensions/browser/mock_display_info_provider.cc b/extensions/browser/mock_display_info_provider.cc
index cfffef2..f2c0f66 100644
--- a/extensions/browser/mock_display_info_provider.cc
+++ b/extensions/browser/mock_display_info_provider.cc
@@ -97,7 +97,7 @@
 
 void MockDisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
     const display::Display& display,
-    extensions::api::system_display::DisplayUnitInfo* unit) {
+    extensions::api::system_display::DisplayUnitInfo* unit) const {
   int64_t id = display.id();
   unit->name = "DISPLAY NAME FOR " + base::NumberToString(id);
   if (id == 1)
diff --git a/extensions/browser/mock_display_info_provider.h b/extensions/browser/mock_display_info_provider.h
index b7b4cc5..9a15c457 100644
--- a/extensions/browser/mock_display_info_provider.h
+++ b/extensions/browser/mock_display_info_provider.h
@@ -76,7 +76,7 @@
   // platform specific method.
   void UpdateDisplayUnitInfoForPlatform(
       const display::Display& display,
-      extensions::api::system_display::DisplayUnitInfo* unit) override;
+      extensions::api::system_display::DisplayUnitInfo* unit) const override;
 
   std::unique_ptr<base::DictionaryValue> set_info_value_;
   std::string set_info_display_id_;
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 50cf7d1..ca7997d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -361,6 +361,12 @@
     return;
   }
 
+  if (self.contentSuggestionsMediator.showingStartSurface) {
+    // Start has already been configured. Don't try again or else another Return
+    // To Recent Tab tile will be added.
+    return;
+  }
+
   // Update Mediator property to signal the NTP is currently showing Start.
   self.contentSuggestionsMediator.showingStartSurface = YES;
   if (ShouldShowReturnToMostRecentTabForStartSurface()) {
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 2b0b735..3c39125 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0b90db9d5697f33849c2438d3b9767280ce05cbd
\ No newline at end of file
+4aaadea629b633b3d1380d07aa9bb3102fdf4043
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index e56611c..cec4b29 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-dd6ee08505361906b7f4913ba13ef3e3f2363b3a
\ No newline at end of file
+2c85e739263cc1dc4cf2ebed626ba6292fde20f1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index c98e567fe..ff1af0e 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ead5a844900abff14b371c784461ca25b1198ce8
\ No newline at end of file
+975b626610e40b610b1ae5e8e018198458a6273f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 2369dfe..16f34d1a 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-3ef0b700d85f24ccaa016baf89fa2d6b7d668b32
\ No newline at end of file
+428a29d462bd1b8b77712c653eb9643ab5d9f013
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
new file mode 100644
index 0000000..6531631
--- /dev/null
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -0,0 +1 @@
+3071e19bff2caee243de3a2f49c2fb637d804538
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
new file mode 100644
index 0000000..7dcd80a8
--- /dev/null
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -0,0 +1 @@
+aeb00f778906835e50da14f0414c3492f8ee4920
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 2a8b54c..380e5e3 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-44a8e99316dd2b46d24748111a97dfaf35e4995f
\ No newline at end of file
+0dcd3cbf37365de798144fca6622cd13cbca7dac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 9f9511a..4de15528e 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9cd5d2f8131ee2c86ac7f225edc9908e6d2ebabb
\ No newline at end of file
+9482aa5cbc15969bf477f7ce10276664225a6474
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 17d5d68c..a18b104 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4b792c38d19fd45dc4f08b00e4f1a523bdd487fb
\ No newline at end of file
+e03bbf2e3771ca4dc43ba6c20374614bc24f8aee
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 47faf0e7..64f7b53 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b817afeab8a33a28c197abe436c3a01d0cbf0530
\ No newline at end of file
+2ca951e52af3031f5ac1a45f85d93f19c047935b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 84948409..2e2dbb55 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-351f842a3289904ee2bd2d03329d912d7cfe01f2
\ No newline at end of file
+1d338aed50c2f2c72476eb7e7e5920977f7dc7d6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 957fc1b..5350a9c 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d813c772cb9c26268bfb28f3eba13ab2289addb2
\ No newline at end of file
+4c81914931f1d00a9ba18f97172aaa950556a721
\ No newline at end of file
diff --git a/media/cast/cast_config.cc b/media/cast/cast_config.cc
index 01e72e5..68373c0b 100644
--- a/media/cast/cast_config.cc
+++ b/media/cast/cast_config.cc
@@ -7,56 +7,32 @@
 namespace media {
 namespace cast {
 
-VideoCodecParams::VideoCodecParams()
-    : max_qp(kDefaultMaxQp),
-      min_qp(kDefaultMinQp),
-      max_cpu_saver_qp(kDefaultMaxCpuSaverQp),
-      max_number_of_video_buffers_used(kDefaultNumberOfVideoBuffers),
-      number_of_encode_threads(1) {}
-
+VideoCodecParams::VideoCodecParams() = default;
 VideoCodecParams::VideoCodecParams(const VideoCodecParams& other) = default;
-
+VideoCodecParams::VideoCodecParams(VideoCodecParams&& other) = default;
+VideoCodecParams& VideoCodecParams::operator=(const VideoCodecParams& other) =
+    default;
+VideoCodecParams& VideoCodecParams::operator=(VideoCodecParams&& other) =
+    default;
 VideoCodecParams::~VideoCodecParams() = default;
 
-FrameSenderConfig::FrameSenderConfig()
-    : sender_ssrc(0),
-      receiver_ssrc(0),
-      // In production, these values are overridden by the mirror settings
-      // and potentially the mirroring session parameters, however we provide
-      // a reasonable default here for some use cases, such as tests.
-      // All three delays are set to the same value due to adaptive latency
-      // being disabled in Chrome. This will be fixed as part of the migration
-      // to libcast.
-      min_playout_delay(kDefaultTargetPlayoutDelay),
-      max_playout_delay(kDefaultTargetPlayoutDelay),
-      animated_playout_delay(min_playout_delay),
-      rtp_payload_type(RtpPayloadType::UNKNOWN),
-      use_external_encoder(false),
-      rtp_timebase(0),
-      channels(0),
-      max_bitrate(0),
-      min_bitrate(0),
-      start_bitrate(0),
-      max_frame_rate(kDefaultMaxFrameRate),
-      codec(CODEC_UNKNOWN) {}
-
+FrameSenderConfig::FrameSenderConfig() = default;
 FrameSenderConfig::FrameSenderConfig(const FrameSenderConfig& other) = default;
-
+FrameSenderConfig::FrameSenderConfig(FrameSenderConfig&& other) = default;
+FrameSenderConfig& FrameSenderConfig::operator=(
+    const FrameSenderConfig& other) = default;
+FrameSenderConfig& FrameSenderConfig::operator=(FrameSenderConfig&& other) =
+    default;
 FrameSenderConfig::~FrameSenderConfig() = default;
 
-FrameReceiverConfig::FrameReceiverConfig()
-    : receiver_ssrc(0),
-      sender_ssrc(0),
-      rtp_max_delay_ms(kDefaultTargetPlayoutDelay.InMilliseconds()),
-      rtp_payload_type(RtpPayloadType::UNKNOWN),
-      rtp_timebase(0),
-      channels(0),
-      target_frame_rate(0),
-      codec(CODEC_UNKNOWN) {}
-
+FrameReceiverConfig::FrameReceiverConfig() = default;
 FrameReceiverConfig::FrameReceiverConfig(const FrameReceiverConfig& other) =
     default;
-
+FrameReceiverConfig::FrameReceiverConfig(FrameReceiverConfig&& other) = default;
+FrameReceiverConfig& FrameReceiverConfig::operator=(
+    const FrameReceiverConfig& other) = default;
+FrameReceiverConfig& FrameReceiverConfig::operator=(
+    FrameReceiverConfig&& other) = default;
 FrameReceiverConfig::~FrameReceiverConfig() = default;
 
 }  // namespace cast
diff --git a/media/cast/cast_config.h b/media/cast/cast_config.h
index 9151752..f0c5a90 100644
--- a/media/cast/cast_config.h
+++ b/media/cast/cast_config.h
@@ -110,10 +110,13 @@
 struct VideoCodecParams {
   VideoCodecParams();
   VideoCodecParams(const VideoCodecParams& other);
+  VideoCodecParams(VideoCodecParams&& other);
+  VideoCodecParams& operator=(const VideoCodecParams& other);
+  VideoCodecParams& operator=(VideoCodecParams&& other);
   ~VideoCodecParams();
 
-  int max_qp;
-  int min_qp;
+  int max_qp = kDefaultMaxQp;
+  int min_qp = kDefaultMinQp;
 
   // The maximum |min_quantizer| set to the encoder when CPU is constrained.
   // This is a trade-off between higher resolution with lower encoding quality
@@ -122,7 +125,7 @@
   // at this resolution than lowering resolution with similar CPU usage and
   // smaller quantizer. The set value has to be between |min_qp| and |max_qp|.
   // Suggested value range: [4, 30]. It is only used by software VP8 codec.
-  int max_cpu_saver_qp;
+  int max_cpu_saver_qp = kDefaultMaxCpuSaverQp;
 
   // This field is used differently by various encoders.
   //
@@ -134,21 +137,24 @@
   // may hold before emitting a frame. A larger window may allow higher encoding
   // efficiency at the cost of latency and memory. Set to 0 to let the encoder
   // choose a suitable value for the platform and other encoding settings.
-  int max_number_of_video_buffers_used;
+  int max_number_of_video_buffers_used = kDefaultNumberOfVideoBuffers;
 
-  int number_of_encode_threads;
+  int number_of_encode_threads = 1;
 };
 
 struct FrameSenderConfig {
   FrameSenderConfig();
   FrameSenderConfig(const FrameSenderConfig& other);
+  FrameSenderConfig(FrameSenderConfig&& other);
+  FrameSenderConfig& operator=(const FrameSenderConfig& other);
+  FrameSenderConfig& operator=(FrameSenderConfig&& other);
   ~FrameSenderConfig();
 
   // The sender's SSRC identifier.
-  uint32_t sender_ssrc;
+  uint32_t sender_ssrc = 0;
 
   // The receiver's SSRC identifier.
-  uint32_t receiver_ssrc;
+  uint32_t receiver_ssrc = 0;
 
   // The total amount of time between a frame's capture/recording on the sender
   // and its playback on the receiver (i.e., shown to a user).  This should be
@@ -156,39 +162,43 @@
   // transmit/retransmit, receive, decode, and render; given its run-time
   // environment (sender/receiver hardware performance, network conditions,
   // etc.).
-  base::TimeDelta min_playout_delay;
-  base::TimeDelta max_playout_delay;
+  //
+  // All three delays are set to the same value due to adaptive latency
+  // being disabled in Chrome.
+  // TODO(https://crbug.com/1363017): re-enable adaptive playout dleay.
+  base::TimeDelta min_playout_delay = kDefaultTargetPlayoutDelay;
+  base::TimeDelta max_playout_delay = kDefaultTargetPlayoutDelay;
 
   // Starting playout delay when streaming animated content.
   // TODO(https://crbug.com/1363694): animated playout delay should be removed.
-  base::TimeDelta animated_playout_delay;
+  base::TimeDelta animated_playout_delay = kDefaultTargetPlayoutDelay;
 
   // RTP payload type enum: Specifies the type/encoding of frame data.
-  RtpPayloadType rtp_payload_type;
+  RtpPayloadType rtp_payload_type = RtpPayloadType::UNKNOWN;
 
   // If true, use an external HW encoder rather than the built-in
   // software-based one.
-  bool use_external_encoder;
+  bool use_external_encoder = false;
 
   // RTP timebase: The number of RTP units advanced per one second.  For audio,
   // this is the sampling rate.  For video, by convention, this is 90 kHz.
-  int rtp_timebase;
+  int rtp_timebase = 0;
 
   // Number of channels.  For audio, this is normally 2.  For video, this must
   // be 1 as Cast does not have support for stereoscopic video.
-  int channels;
+  int channels = 0;
 
   // For now, only fixed bitrate is used for audio encoding. So for audio,
   // |max_bitrate| is used, and the other two will be overriden if they are not
   // equal to |max_bitrate|.
-  int max_bitrate;
-  int min_bitrate;
-  int start_bitrate;
+  int max_bitrate = 0;
+  int min_bitrate = 0;
+  int start_bitrate = 0;
 
-  double max_frame_rate;
+  double max_frame_rate = kDefaultMaxFrameRate;
 
   // Codec used for the compression of signal data.
-  Codec codec;
+  Codec codec = CODEC_UNKNOWN;
 
   // The AES crypto key and initialization vector.  Each of these strings
   // contains the data in binary form, of size kAesKeySize.  If they are empty
@@ -198,7 +208,7 @@
 
   // When true, allows use of CODEC_VIDEO_FAKE.  When false, CODEC_VIDEO_FAKE is
   // not supported.
-  bool enable_fake_codec_for_tests{false};
+  bool enable_fake_codec_for_tests = false;
 
   // These are codec specific parameters for video streams only.
   VideoCodecParams video_codec_params;
@@ -207,13 +217,16 @@
 struct FrameReceiverConfig {
   FrameReceiverConfig();
   FrameReceiverConfig(const FrameReceiverConfig& other);
+  FrameReceiverConfig(FrameReceiverConfig&& other);
+  FrameReceiverConfig& operator=(const FrameReceiverConfig& other);
+  FrameReceiverConfig& operator=(FrameReceiverConfig&& other);
   ~FrameReceiverConfig();
 
   // The receiver's SSRC identifier.
-  uint32_t receiver_ssrc;
+  uint32_t receiver_ssrc = 0;
 
   // The sender's SSRC identifier.
-  uint32_t sender_ssrc;
+  uint32_t sender_ssrc = 0;
 
   // The total amount of time between a frame's capture/recording on the sender
   // and its playback on the receiver (i.e., shown to a user).  This is fixed as
@@ -221,26 +234,26 @@
   // transmit/retransmit, receive, decode, and render; given its run-time
   // environment (sender/receiver hardware performance, network conditions,
   // etc.).
-  int rtp_max_delay_ms;
+  int rtp_max_delay_ms = kDefaultTargetPlayoutDelay.InMilliseconds();
 
   // RTP payload type enum: Specifies the type/encoding of frame data.
-  RtpPayloadType rtp_payload_type;
+  RtpPayloadType rtp_payload_type = RtpPayloadType::UNKNOWN;
 
   // RTP timebase: The number of RTP units advanced per one second.  For audio,
   // this is the sampling rate.  For video, by convention, this is 90 kHz.
-  int rtp_timebase;
+  int rtp_timebase = 0;
 
   // Number of channels.  For audio, this is normally 2.  For video, this must
   // be 1 as Cast does not have support for stereoscopic video.
-  int channels;
+  int channels = 0;
 
   // The target frame rate.  For audio, this is normally 100 (i.e., frames have
   // a duration of 10ms each).  For video, this is normally 30, but any frame
   // rate is supported.
-  double target_frame_rate;
+  double target_frame_rate = 0;
 
   // Codec used for the compression of signal data.
-  Codec codec;
+  Codec codec = CODEC_UNKNOWN;
 
   // The AES crypto key and initialization vector.  Each of these strings
   // contains the data in binary form, of size kAesKeySize.  If they are empty
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index 4516f9c..b0877b9 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -476,8 +476,16 @@
 
     constexpr auto kNumGlobalMotionParams = std::size(decltype(gm.params){});
 
-    for (size_t j = 0; j < kNumGlobalMotionParams; ++j)
-      v4l2_gm->params[i][j] = base::checked_cast<uint32_t>(gm.params[j]);
+    for (size_t j = 0; j < kNumGlobalMotionParams; ++j) {
+      // TODO(b/247611513): Remove separate handling when gm.params[j] < 0 if
+      // V4L2 AV1 uAPI decides to make an update to make this param consistent
+      // with definition in libgav1 parser
+      if (gm.params[j] < 0) {
+        v4l2_gm->params[i][j] =
+            base::checked_cast<uint32_t>(UINT32_MAX + gm.params[j] + 1);
+      } else
+        v4l2_gm->params[i][j] = base::checked_cast<uint32_t>(gm.params[j]);
+    }
 
     v4l2_gm[i].invalid = !libgav1::SetupShear(&gm);
   }
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_av1.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_av1.cc
index 560dbda..0118d956 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_av1.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_delegate_av1.cc
@@ -234,6 +234,20 @@
   v4l2_quant.delta_q_res = frm_header.delta_q.scale;
 }
 
+// Section 5.9.18. Loop filter delta parameters syntax.
+// Note that |delta_lf_res| in |v4l2_av1_loop_filter| corresponds to
+// |delta_lf.scale| in the frame header defined in libgav1.
+void FillLoopFilterDeltaParams(struct v4l2_av1_loop_filter& v4l2_lf,
+                               const libgav1::Delta& delta_lf) {
+  if (delta_lf.present)
+    v4l2_lf.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT;
+
+  if (delta_lf.multi)
+    v4l2_lf.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI;
+
+  v4l2_lf.delta_lf_res = delta_lf.scale;
+}
+
 V4L2VideoDecoderDelegateAV1::V4L2VideoDecoderDelegateAV1(
     V4L2DecodeSurfaceHandler* surface_handler,
     V4L2Device* device)
@@ -269,6 +283,8 @@
   struct v4l2_av1_loop_filter v4l2_lf = {};
   FillLoopFilterParams(v4l2_lf, frame_header.loop_filter);
 
+  FillLoopFilterDeltaParams(v4l2_lf, frame_header.delta_lf);
+
   struct v4l2_av1_quantization v4l2_quant = {};
   FillQuantizationParams(v4l2_quant, frame_header.quantizer);
 
@@ -293,4 +309,4 @@
   return true;
 }
 
-}  // namespace media
+}  // namespace media
\ No newline at end of file
diff --git a/mojo/core/channel_posix.cc b/mojo/core/channel_posix.cc
index fe315d4c..ed111dd0 100644
--- a/mojo/core/channel_posix.cc
+++ b/mojo/core/channel_posix.cc
@@ -7,7 +7,6 @@
 #include <errno.h>
 #include <sys/socket.h>
 
-#include <algorithm>
 #include <atomic>
 #include <limits>
 #include <memory>
@@ -21,6 +20,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_pump_for_io.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/ranges/algorithm.h"
 #include "base/synchronization/lock.h"
 #include "base/task/current_thread.h"
 #include "base/task/task_runner.h"
@@ -682,9 +682,7 @@
   if (!num_fds)
     return false;
 
-  auto start = std::find_if(
-      fds_to_close_.begin(), fds_to_close_.end(),
-      [&fds](const base::ScopedFD& fd) { return fd.get() == fds[0]; });
+  auto start = base::ranges::find(fds_to_close_, fds[0], &base::ScopedFD::get);
   if (start == fds_to_close_.end())
     return false;
 
diff --git a/net/socket/udp_socket_unittest.cc b/net/socket/udp_socket_unittest.cc
index 8ac99fed..a88f25f 100644
--- a/net/socket/udp_socket_unittest.cc
+++ b/net/socket/udp_socket_unittest.cc
@@ -866,7 +866,6 @@
   // ConnectUsingNetwork() and won't send any datagrams.
   const IPEndPoint fake_server_address(IPAddress::IPv4Localhost(), 8080);
   const handles::NetworkHandle wrong_network_handle = 65536;
-  TestCompletionCallback callback;
 #if BUILDFLAG(IS_ANDROID)
   NetworkChangeNotifierFactoryAndroid ncn_factory;
   NetworkChangeNotifier::DisableForTest ncn_disable_for_test;
@@ -879,6 +878,7 @@
     // ERR_NOT_IMPLEMENTED when network handles are supported.
     UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr,
                            NetLogSource());
+    TestCompletionCallback callback;
     int rv = socket.ConnectUsingNetworkAsync(
         wrong_network_handle, fake_server_address, callback.callback());
 
@@ -911,6 +911,7 @@
   }
 #else
   UDPClientSocket socket(DatagramSocket::RANDOM_BIND, nullptr, NetLogSource());
+  TestCompletionCallback callback;
   EXPECT_EQ(ERR_NOT_IMPLEMENTED, socket.ConnectUsingNetworkAsync(
                                      wrong_network_handle, fake_server_address,
                                      callback.callback()));
diff --git a/remoting/host/desktop_resizer_x11.cc b/remoting/host/desktop_resizer_x11.cc
index 6ae32da5..29a38889 100644
--- a/remoting/host/desktop_resizer_x11.cc
+++ b/remoting/host/desktop_resizer_x11.cc
@@ -301,13 +301,6 @@
       }
       auto crtc = output_info.crtcs.front();
       auto track_layout = diff.new_displays[i];
-      // Note that this has a weird behavior in GNOME, such that, if |output| is
-      // "disconnected", creating the mode somehow resizes all existing displays
-      // to 1024x768. Once the output is successfully enabled, it will remain
-      // "connected" and will no longer have the problem. The problem doesn't
-      // occur on XFCE or Cinnamon.
-      // TODO(yuweih): See if this is fixable, or at least implement some
-      // workaround, such as re-applying the layout.
       auto mode =
           UpdateMode(output, track_layout.width(), track_layout.height());
       if (mode == kInvalidMode) {
diff --git a/remoting/host/installer/linux/BUILD.gn b/remoting/host/installer/linux/BUILD.gn
index 6cda9aa..cd2dad6d 100644
--- a/remoting/host/installer/linux/BUILD.gn
+++ b/remoting/host/installer/linux/BUILD.gn
@@ -64,6 +64,8 @@
     "//remoting/host/installer/linux/Xsession",
     "//remoting/host/installer/linux/is-remoting-session",
     "//remoting/host/linux/configure_url_forwarder.py",
+    "//remoting/host/linux/connect_xrandr_outputs.sh",
+    "//remoting/host/linux/crd-connect-xrandr-outputs.desktop",
     "//remoting/host/linux/linux_me2me_host.py",
     "$root_gen_dir/remoting/CREDITS.txt",
     "$root_out_dir/icudtl.dat",
diff --git a/remoting/host/installer/linux/Makefile b/remoting/host/installer/linux/Makefile
index 546a66e9..b6c334d 100644
--- a/remoting/host/installer/linux/Makefile
+++ b/remoting/host/installer/linux/Makefile
@@ -17,6 +17,7 @@
 CHROME_NATIVE_MESSAGING_DIR = $(DESTDIR)/etc/opt/chrome/native-messaging-hosts
 FIREFOX_NATIVE_MESSAGING_DIR = $(DESTDIR)/usr/lib/mozilla/native-messaging-hosts
 SYSTEM_WIDE_DESKTOP_ENTRIES_DIR = $(DESTDIR)/usr/share/applications
+XDG_AUTOSTART_DIR=$(DESTDIR)/etc/xdg/autostart
 
 CORE_LIBNAME = $(BUILD_DIR)/libremoting_core.so
 CORE_DEBUGFILE = $(CORE_LIBNAME).debug
@@ -49,6 +50,7 @@
 	install -d "$(CHROME_NATIVE_MESSAGING_DIR)"
 	install -d "$(FIREFOX_NATIVE_MESSAGING_DIR)"
 	install -d "$(SYSTEM_WIDE_DESKTOP_ENTRIES_DIR)"
+	install -d "$(XDG_AUTOSTART_DIR)"
 
 	install "$(SRC_DIR)/remoting/host/linux/linux_me2me_host.py" \
 	  "$(INSTALL_DIR)/chrome-remote-desktop"
@@ -58,6 +60,8 @@
 	  "$(INSTALL_DIR)"
 	install "$(SRC_DIR)/remoting/host/linux/configure_url_forwarder.py" \
 	  "$(INSTALL_DIR)/configure-url-forwarder"
+	install "$(SRC_DIR)/remoting/host/linux/connect_xrandr_outputs.sh" \
+	  "$(INSTALL_DIR)/connect-xrandr-outputs"
 
 	install -m 0644 \
 	  "$(BUILD_DIR)/remoting/com.google.chrome.remote_desktop.json" \
@@ -113,6 +117,10 @@
 	  "$(SYSTEM_WIDE_DESKTOP_ENTRIES_DIR)/crd-url-forwarder.desktop"
 
 	install -m 0644 \
+	  "$(SRC_DIR)/remoting/host/linux/crd-connect-xrandr-outputs.desktop" \
+	  "$(XDG_AUTOSTART_DIR)/crd-connect-xrandr-outputs.desktop"
+
+	install -m 0644 \
 	  "$(BUILD_DIR)/icudtl.dat" "$(INSTALL_DIR)/icudtl.dat"
 
 	for locale in $$(ls $(BUILD_DIR)/remoting_locales); do \
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index f84d144..af3d7e44 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -63,8 +63,19 @@
     outputs = [ "$root_build_dir/remoting/setup-url-forwarder" ]
   }
 
+  copy("connect_xrandr_outputs_script") {
+    sources = [ "connect_xrandr_outputs.sh" ]
+    outputs = [ "$root_build_dir/remoting/connect-xrandr-outputs" ]
+  }
+
+  copy("connect_xrandr_outputs_desktop_entry") {
+    sources = [ "crd-connect-xrandr-outputs.desktop" ]
+    outputs = [ "$root_build_dir/remoting/crd-connect-xrandr-outputs.desktop" ]
+  }
+
   group("remoting_dev_me2me_host") {
     deps = [
+      ":connect_xrandr_outputs_script",
       ":remoting_me2me_host_copy_host_wrapper",
       ":remoting_me2me_host_copy_script",
       ":remoting_me2me_host_copy_setup_url_forwarder_script",
diff --git a/remoting/host/linux/connect_xrandr_outputs.sh b/remoting/host/linux/connect_xrandr_outputs.sh
new file mode 100755
index 0000000..d8d7948
--- /dev/null
+++ b/remoting/host/linux/connect_xrandr_outputs.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# On GNOME we have observed that when a new mode is added to a disconnected
+# randr output, all existing outputs will be resized to 1024x768. This script
+# helps connect xrandr outputs before the host is being used, so that existing
+# displays won't get resized when the user adds displays in a remote session.
+
+WIDTH=1600
+HEIGHT=1200
+MODE="${WIDTH}x${HEIGHT}"
+
+# Create the mode in case it hasn't been created yet.
+xrandr --newmode "$MODE" 60 "$WIDTH" 0 0 0 "$HEIGHT" 0 0 0
+
+# The client supports adding up to 3 displays (plus the existing DUMMY0) so we
+# connect DUMMY 1-3.
+connected_outputs=()
+for n in $(seq 1 3); do
+  query_result="$(xrandr -q)"
+  n_1=$((n - 1))
+  current_output="DUMMY$n"
+  previous_output="DUMMY$n_1"
+
+  if ! echo "$query_result" | grep -q "$previous_output connected"; then
+    >&2 echo "Unexpected state: $previous_output is not connected"
+    exit 1
+  fi
+
+  if echo "$query_result" | grep -q "$current_output connected"; then
+    >&2 echo "$current_output is already connected; no change will be made"
+    continue
+  fi
+
+  xrandr --addmode "$current_output" "$MODE"
+  xrandr --output "$current_output" --mode $MODE --right-of "$previous_output"
+  connected_outputs+=("$current_output")
+  echo "$current_output connected"
+done
+
+# If we turn off displays immediately, GNOME will turn them back on, so we need
+# to wait here.
+echo "Waiting for 2 seconds..."
+sleep 2
+
+# Turn off outputs in reverse order.
+for (( idx=${#connected_outputs[@]}-1 ; idx>=0 ; idx-- )) ; do
+  current_output="${connected_outputs[idx]}"
+  xrandr --output "$current_output" --off
+  echo "$current_output turned off"
+done
diff --git a/remoting/host/linux/crd-connect-xrandr-outputs.desktop b/remoting/host/linux/crd-connect-xrandr-outputs.desktop
new file mode 100644
index 0000000..a51d7acb
--- /dev/null
+++ b/remoting/host/linux/crd-connect-xrandr-outputs.desktop
@@ -0,0 +1,11 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+[Desktop Entry]
+Name="Chrome Remote Desktop XRANDR Output Connector"
+Exec=/opt/google/chrome-remote-desktop/connect-xrandr-outputs
+NoDisplay=true
+Type=Application
+Comment=Helper script to connect XRANDR outputs beforehand so that adding displays in a remote session won't resize existing displays on GNOME.
+OnlyShowIn=GNOME;Cinnamon;X-Cinnamon;
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 524aaf30a2..bbcebcb 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -635,6 +635,8 @@
       ]
     },
   ]
+
+  webui_module_path = "/"
 }
 
 mojom("mojom_network_param") {
diff --git a/storage/browser/test/mock_quota_client.cc b/storage/browser/test/mock_quota_client.cc
index 3f93909..62ca736 100644
--- a/storage/browser/test/mock_quota_client.cc
+++ b/storage/browser/test/mock_quota_client.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/singleton.h"
+#include "base/ranges/algorithm.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/services/storage/public/cpp/buckets/bucket_locator.h"
@@ -47,8 +48,8 @@
     const blink::StorageKey& storage_key,
     blink::mojom::StorageType storage_type,
     int64_t delta) {
-  auto it = std::find_if(
-      bucket_data_.begin(), bucket_data_.end(),
+  auto it = base::ranges::find_if(
+      bucket_data_,
       [storage_key, storage_type](std::pair<BucketLocator, int64_t> entry) {
         return entry.first.is_default &&
                entry.first.storage_key == storage_key &&
@@ -65,10 +66,9 @@
 }
 
 void MockQuotaClient::ModifyBucketAndNotify(BucketId bucket_id, int64_t delta) {
-  auto it = std::find_if(bucket_data_.begin(), bucket_data_.end(),
-                         [bucket_id](std::pair<BucketLocator, int64_t> entry) {
-                           return entry.first.id == bucket_id;
-                         });
+  auto it = base::ranges::find(
+      bucket_data_, bucket_id,
+      [](std::pair<BucketLocator, int64_t> entry) { return entry.first.id; });
   DCHECK(it != bucket_data_.end());
   it->second += delta;
   DCHECK_GE(it->second, 0);
diff --git a/storage/browser/test/mock_quota_manager.cc b/storage/browser/test/mock_quota_manager.cc
index cf12a4d..656c568 100644
--- a/storage/browser/test/mock_quota_manager.cc
+++ b/storage/browser/test/mock_quota_manager.cc
@@ -13,6 +13,7 @@
 #include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
+#include "base/ranges/algorithm.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/bind.h"
 #include "base/test/test_waitable_event.h"
@@ -200,15 +201,13 @@
 bool MockQuotaManager::AddBucket(const BucketInfo& bucket,
                                  QuotaClientTypes quota_client_types,
                                  base::Time modified) {
-  auto it = std::find_if(
-      buckets_.begin(), buckets_.end(),
-      [bucket](const BucketData& bucket_data) {
+  DCHECK(
+      base::ranges::none_of(buckets_, [bucket](const BucketData& bucket_data) {
         return bucket.id == bucket_data.bucket.id ||
                (bucket.name == bucket_data.bucket.name &&
                 bucket.storage_key == bucket_data.bucket.storage_key &&
                 bucket.type == bucket_data.bucket.type);
-      });
-  DCHECK(it == buckets_.end());
+      }));
   buckets_.emplace_back(
       BucketData(bucket, std::move(quota_client_types), modified));
   return true;
@@ -301,10 +300,9 @@
     BucketId bucket,
     bool persistent,
     base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback) {
-  auto it = std::find_if(buckets_.begin(), buckets_.end(),
-                         [bucket](const BucketData& bucket_data) {
-                           return bucket_data.bucket.id == bucket;
-                         });
+  auto it = base::ranges::find(
+      buckets_, bucket,
+      [](const BucketData& bucket_data) { return bucket_data.bucket.id; });
   if (it != buckets_.end()) {
     it->bucket.persistent = persistent;
     std::move(callback).Run(it->bucket);
@@ -324,10 +322,9 @@
 
 QuotaErrorOr<BucketInfo> MockQuotaManager::FindBucketById(
     const BucketId& bucket_id) {
-  auto it = std::find_if(buckets_.begin(), buckets_.end(),
-                         [bucket_id](const BucketData& bucket_data) {
-                           return bucket_data.bucket.id == bucket_id;
-                         });
+  auto it = base::ranges::find(
+      buckets_, bucket_id,
+      [](const BucketData& bucket_data) { return bucket_data.bucket.id; });
   if (it != buckets_.end()) {
     return it->bucket;
   }
@@ -338,8 +335,8 @@
     const blink::StorageKey& storage_key,
     const std::string& bucket_name,
     blink::mojom::StorageType type) {
-  auto it = std::find_if(
-      buckets_.begin(), buckets_.end(),
+  auto it = base::ranges::find_if(
+      buckets_,
       [storage_key, bucket_name, type](const BucketData& bucket_data) {
         return bucket_data.bucket.storage_key == storage_key &&
                bucket_data.bucket.name == bucket_name &&
@@ -354,13 +351,12 @@
 QuotaErrorOr<BucketInfo> MockQuotaManager::FindAndUpdateBucket(
     const BucketInitParams& params,
     blink::mojom::StorageType type) {
-  auto it = std::find_if(buckets_.begin(), buckets_.end(),
-                         [params, type](const BucketData& bucket_data) {
-                           return bucket_data.bucket.storage_key ==
-                                      params.storage_key &&
-                                  bucket_data.bucket.name == params.name &&
-                                  bucket_data.bucket.type == type;
-                         });
+  auto it = base::ranges::find_if(
+      buckets_, [params, type](const BucketData& bucket_data) {
+        return bucket_data.bucket.storage_key == params.storage_key &&
+               bucket_data.bucket.name == params.name &&
+               bucket_data.bucket.type == type;
+      });
   if (it != buckets_.end()) {
     if (params.persistent)
       it->bucket.persistent = *params.persistent;
diff --git a/testing/buildbot/filters/code_coverage.browser_tests.filter b/testing/buildbot/filters/code_coverage.browser_tests.filter
index 971478e5..fc1b26c 100644
--- a/testing/buildbot/filters/code_coverage.browser_tests.filter
+++ b/testing/buildbot/filters/code_coverage.browser_tests.filter
@@ -7,9 +7,6 @@
 -PopupTrackerBrowserTest.ControlClick_HasTracker
 -PrerenderBrowserTest.AutosigninInPrerenderer
 -ProfileStatisticsBrowserTest.GatherStatistics
--TabHoverCardBubbleViewBrowserTest.WidgetNotVisibleOnMousePressAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnTabCloseButtonFocusAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnTabFocus
 -TranslateLanguageBrowserTest.RecentTargetLanguage
 -SaveCardBubbleViewsFullFormBrowserTest.Local_Metrics_SigninImpressionManageCards
 -SaveCardBubbleViewsFullFormBrowserTest.Upload_ClickingSaveClosesBubble
diff --git a/testing/buildbot/filters/fuchsia.browser_tests.filter b/testing/buildbot/filters/fuchsia.browser_tests.filter
index 42f6ae5..33aac4e 100644
--- a/testing/buildbot/filters/fuchsia.browser_tests.filter
+++ b/testing/buildbot/filters/fuchsia.browser_tests.filter
@@ -560,11 +560,6 @@
 -ProxySettingsApiTest.ProxyEventsParseError
 -ServiceWorkerTest.MimeHandlerView
 -StartupMetricsTest.ReportsValues
--TabHoverCardBubbleViewBrowserTest.WidgetNotVisibleOnMousePressAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnKeyPressAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnTabCloseButtonFocusAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnTabFocusFromKeyboardAccelerator
 -WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/SiteIsolationForGuestsDisabled
 -WebViewTests/WebViewTest.DownloadCookieIsolation_CrossSession/SiteIsolationForGuestsEnabled
 -WebViewTests/WebViewTest.NoPrerenderer/SiteIsolationForGuestsDisabled
diff --git a/testing/buildbot/filters/linux-lacros.browser_tests.filter b/testing/buildbot/filters/linux-lacros.browser_tests.filter
index 9ad0f93..2391502 100644
--- a/testing/buildbot/filters/linux-lacros.browser_tests.filter
+++ b/testing/buildbot/filters/linux-lacros.browser_tests.filter
@@ -19,8 +19,6 @@
 -OutOfProcessPPAPITest.Printing
 -ProfileListDesktopBrowserTest.SwitchToProfile
 -SSLUITest.TestCloseTabWithUnsafePopup
--TabHoverCardBubbleViewBrowserTest.WidgetNotVisibleOnMousePressAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnKeyPressAfterTabFocus
 # crbug.com/1121486
 # Following tests were flaky. We disable them first until we have time to investigate.
 -All/HostedOrWebAppTest.CtrlClickLink/HostedApp
diff --git a/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter b/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
index ce6e7e3..37ab05e 100644
--- a/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
+++ b/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
@@ -15,8 +15,6 @@
 -JavaScriptTabModalDialogViewViewsBrowserTest.AlertDialogCloseButtonAccessibilityIgnored
 -PreservedWindowPlacement.Test
 -ProfileHelperTest.OpenNewWindowForProfile
--TabHoverCardBubbleViewBrowserTest.WidgetNotVisibleOnMousePressAfterTabFocus
--TabHoverCardBubbleViewBrowserTest.WidgetVisibleOnKeyPressAfterTabFocus
 
 # Screen capture on Wayland is tricky.
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideo/0
diff --git a/testing/buildbot/filters/pixel_tests.filter b/testing/buildbot/filters/pixel_tests.filter
index 63584d1..b266b6b 100644
--- a/testing/buildbot/filters/pixel_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -70,7 +70,7 @@
 *SendTabToSelfBubbleTest.*
 SessionCrashedBubbleViewTest.*
 TabGroupEditorBubbleViewDialogBrowserTest.*
-TabHoverCardBubbleViewBrowserTest.*
+TabHoverCardBubbleViewDialogBrowserTest.*
 *TailoredSecurityDesktopModalTest.InvokeUi_*
 TranslateBubbleVisualTest.InvokeUi_*
 UpdateRecommendedDialogTest.*
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b2c95e20..036b0fd5 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -11175,21 +11175,6 @@
             ]
         }
     ],
-    "WebApkServiceWorkerRemovalStudy": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SkipServiceWorkerCheckInstallOnly"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebFeedsMVP": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index a89aaea5..c48ad8c 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -164,8 +164,6 @@
     "presentation/presentation.mojom",
     "push_messaging/push_messaging.mojom",
     "push_messaging/push_messaging_status.mojom",
-    "quota/quota_manager_host.mojom",
-    "quota/quota_types.mojom",
     "render_accessibility.mojom",
     "renderer_preference_watcher.mojom",
     "renderer_preferences.mojom",
@@ -200,8 +198,6 @@
     "speech/speech_recognizer.mojom",
     "speech/speech_synthesis.mojom",
     "storage_access/storage_access_automation.mojom",
-    "storage_key/ancestor_chain_bit.mojom",
-    "storage_key/storage_key.mojom",
     "subapps/sub_apps_service.mojom",
     "timing/performance_mark_or_measure.mojom",
     "timing/resource_timing.mojom",
@@ -267,7 +263,9 @@
     "//services/viz/public/mojom",
     "//skia/public/mojom",
     "//third_party/blink/public/mojom/gpu",
+    "//third_party/blink/public/mojom/quota",
     "//third_party/blink/public/mojom/service_worker:storage",
+    "//third_party/blink/public/mojom/storage_key",
     "//third_party/blink/public/mojom/tokens",
     "//third_party/blink/public/mojom/usb",
     "//ui/accessibility/mojom",
@@ -737,15 +735,6 @@
     {
       types = [
         {
-          mojom = "blink.mojom.StorageKey"
-          cpp = "::blink::StorageKey"
-        },
-      ]
-      traits_headers = [ "//third_party/blink/public/common/storage_key/storage_key_mojom_traits.h" ]
-    },
-    {
-      types = [
-        {
           mojom = "blink.mojom.UseCounterFeature"
           cpp = "::blink::UseCounterFeature"
         },
@@ -828,15 +817,6 @@
       ]
       traits_headers = [ "//third_party/blink/renderer/platform/mojo/shared_storage_mojom_traits.h" ]
     },
-    {
-      types = [
-        {
-          mojom = "blink.mojom.StorageKey"
-          cpp = "::blink::BlinkStorageKey"
-        },
-      ]
-      traits_headers = [ "//third_party/blink/renderer/platform/storage/blink_storage_key_mojom_traits.h" ]
-    },
   ]
   cpp_typemaps += shared_cpp_typemaps
   blink_cpp_typemaps += shared_cpp_typemaps
@@ -1095,6 +1075,7 @@
     "//services/viz/public/mojom",
     "//skia/public/mojom",
     "//third_party/blink/public/mojom/service_worker:storage",
+    "//third_party/blink/public/mojom/storage_key",
     "//ui/base/dragdrop/mojom",
     "//ui/base/mojom",
     "//ui/display/mojom",
diff --git a/third_party/blink/public/mojom/quota/BUILD.gn b/third_party/blink/public/mojom/quota/BUILD.gn
new file mode 100644
index 0000000..b931d20
--- /dev/null
+++ b/third_party/blink/public/mojom/quota/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("quota") {
+  sources = [
+    "quota_manager_host.mojom",
+    "quota_types.mojom",
+  ]
+
+  scramble_message_ids = false
+
+  export_class_attribute = "BLINK_COMMON_EXPORT"
+  export_define = "BLINK_COMMON_IMPLEMENTATION=1"
+  export_header = "third_party/blink/public/common/common_export.h"
+
+  export_class_attribute_blink = "PLATFORM_EXPORT"
+  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/renderer/platform/platform_export.h"
+
+  generate_java = true
+
+  webui_module_path = "/"
+}
diff --git a/third_party/blink/public/mojom/storage_key/BUILD.gn b/third_party/blink/public/mojom/storage_key/BUILD.gn
new file mode 100644
index 0000000..f66a85259
--- /dev/null
+++ b/third_party/blink/public/mojom/storage_key/BUILD.gn
@@ -0,0 +1,55 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("storage_key") {
+  sources = [
+    "ancestor_chain_bit.mojom",
+    "storage_key.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//services/network/public/mojom:mojom_schemeful_site",
+    "//url/mojom:url_mojom_gurl",
+    "//url/mojom:url_mojom_origin",
+  ]
+
+  cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "blink.mojom.StorageKey"
+          cpp = "::blink::StorageKey"
+        },
+      ]
+      traits_headers = [ "//third_party/blink/public/common/storage_key/storage_key_mojom_traits.h" ]
+    },
+  ]
+
+  blink_cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "blink.mojom.StorageKey"
+          cpp = "::blink::BlinkStorageKey"
+        },
+      ]
+      traits_headers = [ "//third_party/blink/renderer/platform/storage/blink_storage_key_mojom_traits.h" ]
+    },
+  ]
+
+  export_class_attribute = "BLINK_COMMON_EXPORT"
+  export_define = "BLINK_COMMON_IMPLEMENTATION=1"
+  export_header = "third_party/blink/public/common/common_export.h"
+
+  export_class_attribute_blink = "PLATFORM_EXPORT"
+  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/renderer/platform/platform_export.h"
+
+  generate_java = true
+
+  webui_module_path = "/"
+}
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index c2caf8c..72fb3f9a 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3678,6 +3678,7 @@
   kWebAuthnConditionalUiGet = 4357,
   kWebAuthnConditionalUiGetSuccess = 4358,
   kWebAuthnRkRequiredCreationSuccess = 4359,
+  kDestructiveDocumentWriteAfterModuleScript = 4360,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index a83fea15..c1585890 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -99,16 +99,6 @@
 
   int AxID() const;
 
-  // Update layout if necessary on the underlying tree and return true if the
-  // object is valid. Note that calling this and other methods can cause other
-  // WebAXObjects to become invalid, so always check validity of an object
-  // before using it.
-  bool MaybeUpdateLayoutAndCheckValidity();
-
-  // Return true if this object is still valid (not detached) and has updated
-  // layout.
-  bool CheckValidity();
-
   unsigned ChildCount() const;
 
   WebAXObject ChildAt(unsigned) const;
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
index 5916581..1984338 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -94,6 +94,7 @@
   // Returns true if the AXObject is removed.
   virtual bool Remove(LayoutObject*) = 0;
   virtual void Remove(Node*) = 0;
+  virtual void Remove(Document*) = 0;
   virtual void Remove(AbstractInlineTextBox*) = 0;
 
   virtual const Element* RootAXEditableElement(const Node*) = 0;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d06e20a..b35d8de9 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2915,25 +2915,23 @@
   if (this == &AXObjectCacheOwner()) {
     ax_contexts_.clear();
     ClearAXObjectCache();
+  } else {
+    DCHECK(!ax_object_cache_ || ExistingAXObjectCache())
+        << "Had AXObjectCache for parent, but not for popup document.";
+    if (AXObjectCache* cache = ExistingAXObjectCache()) {
+      // This is a popup document. Clear all accessibility state related to it
+      // by removing the AXObject for its root. The AXObjectCache is
+      // retrieved from the main document, but it maintains both documents.
+      cache->Remove(this);
+    }
   }
+
   computed_node_mapping_.clear();
 
   DetachLayoutTree();
   layout_view_ = nullptr;
   DCHECK(!View()->IsAttached());
 
-  if (this != &AXObjectCacheOwner()) {
-    if (AXObjectCache* cache = ExistingAXObjectCache()) {
-      // Documents that are not a root document use the AXObjectCache in
-      // their root document. Node::removedFrom is called after the
-      // document has been detached so it can't find the root document.
-      // We do the removals here instead.
-      for (Node& node : NodeTraversal::DescendantsOf(*this)) {
-        cache->Remove(&node);
-      }
-    }
-  }
-
   GetStyleEngine().DidDetach();
 
   GetFrame()->GetEventHandlerRegistry().DocumentDetached(*this);
@@ -4144,6 +4142,10 @@
     if (ignore_opens_during_unload_count_)
       return;
 
+    if (ignore_destructive_write_module_script_count_) {
+      UseCounter::Count(*this,
+                        WebFeature::kDestructiveDocumentWriteAfterModuleScript);
+    }
     open(entered_window, ASSERT_NO_EXCEPTION);
   }
 
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index cd5fc6731..1acf82b 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1914,6 +1914,13 @@
   // TODO(https://crbug.com/1296161): Delete this function.
   void CheckPartitionedCookiesOriginTrial(const ResourceResponse& response);
 
+  void IncrementIgnoreDestructiveWriteModuleScriptCount() {
+    ignore_destructive_write_module_script_count_++;
+  }
+  unsigned GetIgnoreDestructiveWriteModuleScriptCount() {
+    return ignore_destructive_write_module_script_count_;
+  }
+
  protected:
   void ClearXMLVersion() { xml_version_ = String(); }
 
@@ -2566,6 +2573,10 @@
   // link header so that they won't be incidentally GC-ed and cancelled.
   HeapHashSet<Member<const PendingLinkPreload>> pending_link_header_preloads_;
 
+  // This is incremented when a module script is evaluated.
+  // http://crbug.com/1079044
+  unsigned ignore_destructive_write_module_script_count_ = 0;
+
   // If you want to add new data members to blink::Document, please reconsider
   // if the members really should be in blink::Document.  document.h is a very
   // popular header, and the size of document.h affects build time
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 281d948a..11c1691 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -436,9 +436,8 @@
     return false;
 
   const uint64_t request_id = request.InspectorId();
-
   scoped_refptr<const SecurityOrigin> reporting_origin =
-      ReportingOriginForUrlIfValid(response.CurrentRequestUrl(),
+      ReportingOriginForUrlIfValid(response.ResponseUrl(),
                                    /*element=*/nullptr, request_id);
   if (!reporting_origin)
     return false;
@@ -563,7 +562,7 @@
     return;
 
   scoped_refptr<const SecurityOrigin> reporting_origin =
-      loader_->ReportingOriginForUrlIfValid(response.CurrentRequestUrl(),
+      loader_->ReportingOriginForUrlIfValid(response.ResponseUrl(),
                                             /*element=*/nullptr, request_id);
   if (!reporting_origin)
     return;
diff --git a/third_party/blink/renderer/core/script/pending_script.cc b/third_party/blink/renderer/core/script/pending_script.cc
index d96e8380..3be37e39 100644
--- a/third_party/blink/renderer/core/script/pending_script.cc
+++ b/third_party/blink/renderer/core/script/pending_script.cc
@@ -247,6 +247,9 @@
     IgnoreDestructiveWriteCountIncrementer incrementer(
         needs_increment ? context_document : nullptr);
 
+    if (script->GetScriptType() == mojom::blink::ScriptType::kModule)
+      context_document->IncrementIgnoreDestructiveWriteModuleScriptCount();
+
     // <spec step="4.A.1">Let old script element be the value to which the
     // script element's node document's currentScript object was most recently
     // set.</spec>
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 7dd2196a..ca53a38 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -432,6 +432,7 @@
     "autofill_assistant/node_signals_test.cc",
     "background_fetch/background_fetch_icon_loader_test.cc",
     "background_fetch/background_fetch_manager_test.cc",
+    "broadcastchannel/broadcast_channel_unittest.cc",
     "cache_storage/cache_test.cc",
     "canvas/canvas2d/canvas_rendering_context_2d_api_test.cc",
     "canvas/canvas2d/canvas_rendering_context_2d_test.cc",
diff --git a/third_party/blink/renderer/modules/DEPS b/third_party/blink/renderer/modules/DEPS
index 4ec7ed39..aa82aa38 100644
--- a/third_party/blink/renderer/modules/DEPS
+++ b/third_party/blink/renderer/modules/DEPS
@@ -46,6 +46,9 @@
     ".*_fuzzer.cc": [
         "+testing/libfuzzer/proto/lpm_interface.h",
     ],
+    ".*_(unit)?test\.cc": [
+        "+base/run_loop.h",
+    ],
     "canvas_fuzzer.cc": [
         "+base/test/bind.h",
     ],
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 822434a9..00e79c82 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1760,6 +1760,17 @@
   }
 }
 
+void AXObjectCacheImpl::Remove(Document* document) {
+  DCHECK(IsPopup(*document)) << "Call Dispose() to remove the main document.";
+  for (Node* node = document; node;
+       node = LayoutTreeBuilderTraversal::Next(*node, nullptr)) {
+    Remove(node);
+  }
+  notifications_to_post_popup_.clear();
+  tree_update_callback_queue_popup_.clear();
+  invalidated_ids_popup_.clear();
+}
+
 // This is safe to call even if there isn't a current mapping.
 void AXObjectCacheImpl::Remove(AbstractInlineTextBox* inline_text_box) {
   if (!inline_text_box)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index 895aafe..efd9294 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -134,6 +134,7 @@
   // Returns false if no associated AXObject exists in the cache.
   bool Remove(LayoutObject*) override;
   void Remove(Node*) override;
+  void Remove(Document*) override;
   void Remove(AbstractInlineTextBox*) override;
   void Remove(AXObject*);  // Calls more specific Remove methods as necessary.
   // For any ancestor that could contain the passed-in AXObject* in their cached
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
index b94b21a..35c1e7e 100644
--- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
+++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
@@ -5,6 +5,8 @@
 #include "third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h"
 
 #include "base/notreached.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
@@ -122,6 +124,7 @@
   msg.message = std::move(value);
   msg.sender_origin = std::move(sender_origin);
   msg.sender_agent_cluster_id = sender_agent_cluster_id;
+  msg.locked_to_sender_agent_cluster = msg.message->IsLockedToAgentCluster();
   remote_client_->OnMessage(std::move(msg));
 }
 
@@ -186,6 +189,29 @@
 
 BroadcastChannel::BroadcastChannel(ExecutionContext* execution_context,
                                    const String& name)
+    : BroadcastChannel(execution_context,
+                       name,
+                       mojo::NullAssociatedReceiver(),
+                       mojo::NullAssociatedRemote()) {}
+
+BroadcastChannel::BroadcastChannel(
+    base::PassKey<BroadcastChannelTester>,
+    ExecutionContext* execution_context,
+    const String& name,
+    mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+        receiver,
+    mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient> remote)
+    : BroadcastChannel(execution_context,
+                       name,
+                       std::move(receiver),
+                       std::move(remote)) {}
+
+BroadcastChannel::BroadcastChannel(
+    ExecutionContext* execution_context,
+    const String& name,
+    mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+        receiver,
+    mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient> remote)
     : ExecutionContextLifecycleObserver(execution_context),
       name_(name),
       feature_handle_for_scheduler_(
@@ -215,34 +241,26 @@
   //    shared remote for all BroadcastChannel objects created on that thread to
   //    ensure in-order delivery of messages to the appropriate *WorkerHost
   //    object.
-  if (execution_context->IsWindow()) {
-    LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(execution_context);
-    DCHECK(window);
-
+  if (receiver.is_valid() && remote.is_valid()) {
+    receiver_.Bind(std::move(receiver));
+    remote_client_.Bind(std::move(remote));
+  } else if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) {
     LocalFrame* frame = window->GetFrame();
-    if (!frame) {
+    if (!frame)
       return;
-    }
 
     frame->GetRemoteNavigationAssociatedInterfaces()->GetInterface(
         associated_remote_.BindNewEndpointAndPassReceiver());
-
     associated_remote_->ConnectToChannel(
         name_, receiver_.BindNewEndpointAndPassRemote(),
         remote_client_.BindNewEndpointAndPassReceiver());
-
-  } else if (execution_context->IsWorkerGlobalScope()) {
-    WorkerGlobalScope* worker_global_scope =
-        DynamicTo<WorkerGlobalScope>(execution_context);
-    DCHECK(worker_global_scope);
-
-    if (worker_global_scope->IsClosing()) {
+  } else if (auto* worker_global_scope =
+                 DynamicTo<WorkerGlobalScope>(execution_context)) {
+    if (worker_global_scope->IsClosing())
       return;
-    }
 
     mojo::Remote<mojom::blink::BroadcastChannelProvider>& provider =
         GetWorkerThreadSpecificProvider(*worker_global_scope);
-
     provider->ConnectToChannel(name_, receiver_.BindNewEndpointAndPassRemote(),
                                remote_client_.BindNewEndpointAndPassReceiver());
   } else {
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h
index 297fe15..d274d7129 100644
--- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h
+++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h
@@ -11,17 +11,20 @@
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/prefinalizer.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 
 namespace blink {
 
+class BroadcastChannelTester;
 class ScriptValue;
 
-class BroadcastChannel final : public EventTargetWithInlineData,
-                               public ActiveScriptWrappable<BroadcastChannel>,
-                               public ExecutionContextLifecycleObserver,
-                               public mojom::blink::BroadcastChannelClient {
+class MODULES_EXPORT BroadcastChannel final
+    : public EventTargetWithInlineData,
+      public ActiveScriptWrappable<BroadcastChannel>,
+      public ExecutionContextLifecycleObserver,
+      public mojom::blink::BroadcastChannelClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_PRE_FINALIZER(BroadcastChannel, Dispose);
 
@@ -31,6 +34,14 @@
                                   ExceptionState&);
 
   BroadcastChannel(ExecutionContext*, const String& name);
+  BroadcastChannel(
+      base::PassKey<BroadcastChannelTester>,
+      ExecutionContext*,
+      const String& name,
+      mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+          receiver,
+      mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient>
+          remote);
 
   BroadcastChannel(const BroadcastChannel&) = delete;
   BroadcastChannel& operator=(const BroadcastChannel&) = delete;
@@ -60,6 +71,14 @@
   void Trace(Visitor*) const override;
 
  private:
+  BroadcastChannel(
+      ExecutionContext*,
+      const String& name,
+      mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+          receiver,
+      mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient>
+          remote);
+
   void PostMessageInternal(
       scoped_refptr<SerializedScriptValue> value,
       scoped_refptr<SecurityOrigin> sender_origin,
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc
new file mode 100644
index 0000000..cf133e0
--- /dev/null
+++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h"
+
+#include <iterator>
+
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+#include "third_party/blink/renderer/core/event_type_names.h"
+#include "third_party/blink/renderer/core/events/message_event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/messaging/blink_cloneable_message.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class BroadcastChannelTester : public GarbageCollected<BroadcastChannelTester>,
+                               public mojom::blink::BroadcastChannelClient {
+ public:
+  explicit BroadcastChannelTester(ExecutionContext* execution_context)
+      : receiver_(this, execution_context), remote_(execution_context) {
+    // Ideally, these would share a pipe. This is more convenient.
+    mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+        receiver0;
+    mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient>
+        remote0 = receiver0.InitWithNewEndpointAndPassRemote();
+    receiver0.EnableUnassociatedUsage();
+    mojo::PendingAssociatedReceiver<mojom::blink::BroadcastChannelClient>
+        receiver1;
+    mojo::PendingAssociatedRemote<mojom::blink::BroadcastChannelClient>
+        remote1 = receiver1.InitWithNewEndpointAndPassRemote();
+    receiver1.EnableUnassociatedUsage();
+
+    scoped_refptr<base::SequencedTaskRunner> task_runner =
+        execution_context->GetTaskRunner(TaskType::kInternalTest);
+    receiver_.Bind(std::move(receiver0), task_runner);
+    remote_.Bind(std::move(remote1), task_runner);
+    channel_ = MakeGarbageCollected<BroadcastChannel>(
+        base::PassKey<BroadcastChannelTester>(), execution_context,
+        "BroadcastChannelTester", std::move(receiver1), std::move(remote0));
+
+    auto* listener = MakeGarbageCollected<EventListener>(this);
+    channel_->addEventListener(event_type_names::kMessage, listener);
+    channel_->addEventListener(event_type_names::kMessageerror, listener);
+  }
+
+  BroadcastChannel* channel() const { return channel_; }
+  const HeapVector<Member<MessageEvent>>& received_events() const {
+    return received_events_;
+  }
+  const Vector<BlinkCloneableMessage>& sent_messages() const {
+    return sent_messages_;
+  }
+
+  void AwaitNextUpdate(base::OnceClosure closure) {
+    on_next_update_.push_back(std::move(closure));
+  }
+
+  void PostMessage(BlinkCloneableMessage message) {
+    remote_->OnMessage(std::move(message));
+  }
+
+  void Trace(Visitor* visitor) const {
+    visitor->Trace(channel_);
+    visitor->Trace(received_events_);
+    visitor->Trace(receiver_);
+    visitor->Trace(remote_);
+  }
+
+  // BroadcastChannelClient
+  void OnMessage(BlinkCloneableMessage message) override {
+    sent_messages_.push_back(std::move(message));
+    ReportUpdate();
+  }
+
+ private:
+  class EventListener : public NativeEventListener {
+   public:
+    explicit EventListener(BroadcastChannelTester* tester) : tester_(tester) {}
+    void Trace(Visitor* visitor) const override {
+      NativeEventListener::Trace(visitor);
+      visitor->Trace(tester_);
+    }
+
+    void Invoke(ExecutionContext*, Event* event) override {
+      tester_->received_events_.push_back(static_cast<MessageEvent*>(event));
+      tester_->ReportUpdate();
+    }
+
+   private:
+    Member<BroadcastChannelTester> tester_;
+  };
+
+  void ReportUpdate() {
+    Vector<base::OnceClosure> closures = std::move(on_next_update_);
+    for (auto& closure : closures)
+      std::move(closure).Run();
+  }
+
+  Member<BroadcastChannel> channel_;
+  HeapVector<Member<MessageEvent>> received_events_;
+  Vector<BlinkCloneableMessage> sent_messages_;
+  Vector<base::OnceClosure> on_next_update_;
+  HeapMojoAssociatedReceiver<mojom::blink::BroadcastChannelClient,
+                             BroadcastChannelTester>
+      receiver_;
+  HeapMojoAssociatedRemote<mojom::blink::BroadcastChannelClient> remote_;
+};
+
+namespace {
+
+BlinkCloneableMessage MakeNullMessage() {
+  BlinkCloneableMessage message;
+  message.message = SerializedScriptValue::NullValue();
+  message.sender_agent_cluster_id = base::UnguessableToken::Create();
+  return message;
+}
+
+TEST(BroadcastChannelTest, DispatchMessageEvent) {
+  DummyPageHolder holder;
+  ExecutionContext* execution_context = holder.GetFrame().DomWindow();
+  auto* tester =
+      MakeGarbageCollected<BroadcastChannelTester>(execution_context);
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  tester->PostMessage(MakeNullMessage());
+  run_loop.Run();
+
+  ASSERT_EQ(tester->received_events().size(), 1u);
+  EXPECT_EQ(tester->received_events()[0]->type(), event_type_names::kMessage);
+}
+
+TEST(BroadcastChannelTest, AgentClusterLockedMatch) {
+  DummyPageHolder holder;
+  ExecutionContext* execution_context = holder.GetFrame().DomWindow();
+  auto* tester =
+      MakeGarbageCollected<BroadcastChannelTester>(execution_context);
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  BlinkCloneableMessage message = MakeNullMessage();
+  message.sender_agent_cluster_id = execution_context->GetAgentClusterID();
+  message.locked_to_sender_agent_cluster = true;
+  tester->PostMessage(std::move(message));
+  run_loop.Run();
+
+  ASSERT_EQ(tester->received_events().size(), 1u);
+  EXPECT_EQ(tester->received_events()[0]->type(), event_type_names::kMessage);
+}
+
+TEST(BroadcastChannelTest, AgentClusterLockedMismatch) {
+  DummyPageHolder holder;
+  ExecutionContext* execution_context = holder.GetFrame().DomWindow();
+  auto* tester =
+      MakeGarbageCollected<BroadcastChannelTester>(execution_context);
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  BlinkCloneableMessage message = MakeNullMessage();
+  message.locked_to_sender_agent_cluster = true;
+  tester->PostMessage(std::move(message));
+  run_loop.Run();
+
+  ASSERT_EQ(tester->received_events().size(), 1u);
+  EXPECT_EQ(tester->received_events()[0]->type(),
+            event_type_names::kMessageerror);
+}
+
+TEST(BroadcastChannelTest, OutgoingMessagesMarkedWithAgentClusterId) {
+  DummyPageHolder holder;
+  ExecutionContext* execution_context = holder.GetFrame().DomWindow();
+  ScriptState* script_state = ToScriptStateForMainWorld(&holder.GetFrame());
+  auto* tester =
+      MakeGarbageCollected<BroadcastChannelTester>(execution_context);
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  {
+    ScriptState::Scope scope(script_state);
+    tester->channel()->postMessage(
+        ScriptValue::CreateNull(script_state->GetIsolate()),
+        ASSERT_NO_EXCEPTION);
+  }
+  run_loop.Run();
+
+  ASSERT_EQ(tester->sent_messages().size(), 1u);
+  EXPECT_EQ(tester->sent_messages()[0].sender_agent_cluster_id,
+            execution_context->GetAgentClusterID());
+  EXPECT_FALSE(tester->sent_messages()[0].locked_to_sender_agent_cluster);
+}
+
+TEST(BroadcastChannelTest, OutgoingAgentClusterLockedMessage) {
+  DummyPageHolder holder;
+  ExecutionContext* execution_context = holder.GetFrame().DomWindow();
+  ScriptState* script_state = ToScriptStateForMainWorld(&holder.GetFrame());
+  v8::Isolate* isolate = script_state->GetIsolate();
+  auto* tester =
+      MakeGarbageCollected<BroadcastChannelTester>(execution_context);
+
+  base::RunLoop run_loop;
+  tester->AwaitNextUpdate(run_loop.QuitClosure());
+  {
+    // WebAssembly modules are always agent cluster locked. This is a trivial
+    // one with no functionality, just the minimal magic and version.
+    static constexpr uint8_t kTrivialModuleBytes[] = {0x00, 0x61, 0x73, 0x6d,
+                                                      0x01, 0x00, 0x00, 0x00};
+    ScriptState::Scope scope(script_state);
+    v8::Local<v8::WasmModuleObject> trivial_module =
+        v8::WasmModuleObject::Compile(isolate, {std::data(kTrivialModuleBytes),
+                                                std::size(kTrivialModuleBytes)})
+            .ToLocalChecked();
+    tester->channel()->postMessage(ScriptValue(isolate, trivial_module),
+                                   ASSERT_NO_EXCEPTION);
+  }
+  run_loop.Run();
+
+  ASSERT_EQ(tester->sent_messages().size(), 1u);
+  EXPECT_EQ(tester->sent_messages()[0].sender_agent_cluster_id,
+            execution_context->GetAgentClusterID());
+  EXPECT_TRUE(tester->sent_messages()[0].locked_to_sender_agent_cluster);
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 44d37cb..eead087 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -154,40 +154,6 @@
   return private_->AXObjectID();
 }
 
-// This method must be called before serializing any accessibility nodes, in
-// order to ensure that layout calls are not made at an unsafe time in the
-// document lifecycle.
-bool WebAXObject::MaybeUpdateLayoutAndCheckValidity() {
-  DCHECK(!IsDetached());
-
-  if (!MaybeUpdateLayoutAndCheckValidity(GetDocument()))
-    return false;
-
-  // Doing a layout can cause this object to be invalid, so check again.
-  return CheckValidity();
-}
-
-// Returns true if the object is valid and can be accessed.
-bool WebAXObject::CheckValidity() {
-  if (IsDetached())
-    return false;
-
-#if DCHECK_IS_ON()
-  Node* node = private_->GetNode();
-  if (!node)
-    return true;
-
-  // Has up-to-date layout info or is display-locked (content-visibility), which
-  // is handled as a special case inside of accessibility code.
-  Document* document = private_->GetDocument();
-  DCHECK(!document->NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(*node) ||
-         DisplayLockUtilities::LockedAncestorPreventingPaint(*node))
-      << "Node needs layout update and is not display locked";
-#endif  // DCHECK_IS_ON()
-
-  return true;
-}
-
 ax::mojom::DefaultActionVerb WebAXObject::Action() const {
   if (IsDetached())
     return ax::mojom::DefaultActionVerb::kNone;
@@ -228,6 +194,16 @@
   if (IsDetached())
     return;
 
+#if DCHECK_IS_ON()
+  if (Node* node = private_->GetNode()) {
+    Document* document = private_->GetDocument();
+    DCHECK(
+        !document->NeedsLayoutTreeUpdateForNodeIncludingDisplayLocked(*node) ||
+        DisplayLockUtilities::LockedAncestorPreventingPaint(*node))
+        << "Node needs layout update and is not display locked";
+  }
+#endif
+
   private_->Serialize(node_data, accessibility_mode);
 }
 
@@ -1373,9 +1349,14 @@
 bool WebAXObject::MaybeUpdateLayoutAndCheckValidity(
     const WebDocument& web_document) {
   const Document* document = web_document.ConstUnwrap<Document>();
-  if (!document || !document->View())
+  if (!document)
     return false;
 
+  DCHECK(document->defaultView());
+  DCHECK(document->GetFrame());
+  DCHECK(document->View());
+  DCHECK(document->ExistingAXObjectCache());
+
   if (document->NeedsLayoutTreeUpdate() || document->View()->NeedsLayout() ||
       document->Lifecycle().GetState() < DocumentLifecycle::kPrePaintClean) {
     // Note: this always alters the lifecycle, because
diff --git a/third_party/blink/tools/run_wpt_tests.py b/third_party/blink/tools/run_wpt_tests.py
index 89d8499..fc4e1a2 100755
--- a/third_party/blink/tools/run_wpt_tests.py
+++ b/third_party/blink/tools/run_wpt_tests.py
@@ -310,6 +310,7 @@
                             help=('Do not run internal WPTs.'))
         parser.add_argument('--flag-specific',
                             choices=sorted(self.port.flag_specific_configs()),
+                            metavar='FLAG_SPECIFIC',
                             help='The name of a flag-specific suite to run.')
 
     def add_metadata_arguments(self, parser):
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 0abfce63..456e1bc 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
@@ -271110,10 +271110,6 @@
       []
      ],
      "abspos": {
-      "flex-abspos-staticpos-align-content-001-expected.txt": [
-       "a4b03b6e3f4885d42439c900b421a77d8fc1410f",
-       []
-      ],
       "flex-abspos-staticpos-align-content-002-expected.txt": [
        "03063a3bcf60c04a2b4fa9f692af19accba9c1ca",
        []
@@ -289594,6 +289590,10 @@
        "5f790b0da30ea92e789a36d0d25527ad6a15f075",
        []
       ],
+      "auto-009-expected.txt": [
+       "f1310970ad3a00704b1942770aca28ddf7a276df",
+       []
+      ],
       "contain-intrinsic-size-001-ref.html": [
        "299930ac35a35a27cbfbe67ebee66c9facb7d2b9",
        []
@@ -310156,7 +310156,7 @@
       []
      ],
      "service-worker-update.https.window-expected.txt": [
-      "2884c271eecfd46ee3fee89707cbd5c81c624939",
+      "9c4a8d8ecd6696543a5d1b4b88a04dbee952c7b8",
       []
      ],
      "websocket.window-expected.txt": [
@@ -347951,7 +347951,7 @@
      []
     ],
     "RTCIceTransport-expected.txt": [
-     "5de6f8abc62cb88744f6fa2691fd3af8bd007a5f",
+     "ea2802c5199b90734a8d0264fe2b9935c58fd98e",
      []
     ],
     "RTCPeerConnection-addIceCandidate-expected.txt": [
@@ -399504,6 +399504,13 @@
        {}
       ]
      ],
+     "scrollable-overflow-vertical-rl-dynamic.html": [
+      "07f322901748c9709c3037d0223a546549cc9cff",
+      [
+       null,
+       {}
+      ]
+     ],
      "scrollbar-gutter-001.html": [
       "4486471cde560c24058fb168f43629f8da833654",
       [
@@ -403356,6 +403363,13 @@
         {}
        ]
       ],
+      "auto-009.html": [
+       "ca5e4985066f6b8ef0edc45e15f71c31da1715c9",
+       [
+        null,
+        {}
+       ]
+      ],
       "contain-intrinsic-size-028.html": [
        "15e8e0e89108b112bc113d6090946307379f53e7",
        [
@@ -419412,6 +419426,13 @@
        {}
       ]
      ],
+     "mouse-event-retarget.html": [
+      "c9ce6240d40cc8e877a0c1f8299a2215e50841cc",
+      [
+       null,
+       {}
+      ]
+     ],
      "no-focus-events-at-clicking-editable-content-in-link.html": [
       "dc08636c467fdfa23caedc2cccb1e5f570a90b82",
       [
@@ -539449,6 +539470,13 @@
        {}
       ]
      ],
+     "view-timeline-snapport.html": [
+      "5d68d37037a344f4b514e5cd937500abbc66e43c",
+      [
+       null,
+       {}
+      ]
+     ],
      "view-timeline-source.tentative.html": [
       "15fbdb5785543e739e5c0b1ff44cc6396cfb09ca",
       [
@@ -572905,7 +572933,7 @@
      ]
     },
     "no-media-call.html": [
-     "17d90ffa15efbb3c61ac28c4007839cfab986d5d",
+     "590571c01299f2e2a5ec8269ff7b8d8e1aaa7eeb",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
deleted file mode 100644
index a4b03b6..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="align-content: baseline"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
-FAIL .container > div 3 assert_equals: 
-<div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetTop expected 5 but got 3
-FAIL .container > div 4 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-FAIL .container > div 9 assert_equals: 
-<div class="container" style="align-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
-FAIL .container > div 10 assert_equals: 
-<div class="container" style="align-content: end"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetTop expected 5 but got 3
-FAIL .container > div 11 assert_equals: 
-<div class="container" style="align-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
-FAIL .container > div 12 assert_equals: 
-<div class="container" style="align-content: flex-end"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetTop expected 5 but got 3
-PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="align-content: baseline"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
-FAIL .container > div 15 assert_equals: 
-<div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetTop expected -3 but got -1
-FAIL .container > div 16 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-FAIL .container > div 21 assert_equals: 
-<div class="container" style="align-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
-FAIL .container > div 22 assert_equals: 
-<div class="container" style="align-content: end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetTop expected -3 but got -1
-FAIL .container > div 23 assert_equals: 
-<div class="container" style="align-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
-FAIL .container > div 24 assert_equals: 
-<div class="container" style="align-content: flex-end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetTop expected -3 but got -1
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009-expected.txt
new file mode 100644
index 0000000..f1310970
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+FAIL Only recording last remembered inline size assert_equals: Using last remembered inline size - clientHeight expected 1 but got 50
+FAIL Only recording last remembered block size assert_equals: Using last remembered block size - clientWidth expected 2 but got 100
+FAIL contain:inline-size prevents recording last remembered inline size assert_equals: Using last remembered block size - clientWidth expected 2 but got 20
+FAIL contain:inline-size can keep previous last remembered inline size assert_equals: Using last remembered sizes from different times - clientWidth expected 100 but got 20
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009.html
new file mode 100644
index 0000000..ca5e498
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-009.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Last remembered size</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#last-remembered">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#content-visibility">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#containment-inline-size">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7529">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7516">
+<meta name="assert" content="Tests that the last remembered size is tracked independently for each axis." />
+
+<style>
+#target {
+  width: max-content;
+  height: max-content;
+  border: 1px solid;
+}
+#target::before {
+  content: "";
+  display: block;
+}
+.content-100-50::before {
+  width: 100px;
+  height: 50px;
+}
+.content-150-75::before {
+  width: 150px;
+  height: 75px;
+}
+.content-skip {
+  content-visibility: hidden;
+}
+.contain-inline-size {
+  contain: inline-size;
+}
+.ciw-auto-2 {
+  contain-intrinsic-width: auto 2px;
+}
+.ciw-auto-20 {
+  contain-intrinsic-width: auto 20px;
+}
+.cih-auto-1 {
+  contain-intrinsic-height: auto 1px;
+}
+.cih-auto-10 {
+  contain-intrinsic-height: auto 10px;
+}
+</style>
+
+<div id="log"></div>
+
+<div id="target"></div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const target = document.getElementById("target");
+
+function checkSize(expectedWidth, expectedHeight, msg) {
+  assert_equals(target.clientWidth, expectedWidth, msg + " - clientWidth");
+  assert_equals(target.clientHeight, expectedHeight, msg + " - clientHeight");
+}
+
+function nextRendering() {
+  return new Promise(resolve => {
+    requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
+  });
+}
+
+promise_test(async function() {
+  target.className = "content-100-50 ciw-auto-20";
+  checkSize(100, 50, "Sizing normally");
+
+  await nextRendering();
+  target.className = "content-skip ciw-auto-2 cih-auto-1";
+  checkSize(100, 1, "Using last remembered inline size");
+}, "Only recording last remembered inline size");
+
+promise_test(async function() {
+  target.className = "content-100-50 cih-auto-10";
+  checkSize(100, 50, "Sizing normally");
+
+  await nextRendering();
+  target.className = "content-skip ciw-auto-2 cih-auto-1";
+  checkSize(2, 50, "Using last remembered block size");
+}, "Only recording last remembered block size");
+
+promise_test(async function() {
+  target.className = "content-100-50";
+  checkSize(100, 50, "Sizing normally");
+
+  await nextRendering();
+  target.className = "content-100-50 ciw-auto-20 cih-auto-10 contain-inline-size";
+  checkSize(20, 50, "Size containment for inline axis");
+
+  await nextRendering();
+  target.className = "content-skip ciw-auto-2 cih-auto-1";
+  checkSize(2, 50, "Using last remembered block size");
+}, "contain:inline-size prevents recording last remembered inline size");
+
+promise_test(async function() {
+  target.className = "content-100-50 ciw-auto-20 cih-auto-10";
+  checkSize(100, 50, "Sizing normally");
+
+  await nextRendering();
+  target.className = "content-150-75 ciw-auto-20 cih-auto-10 contain-inline-size";
+  checkSize(20, 75, "Size containment for inline axis");
+
+  await nextRendering();
+  target.className = "content-skip ciw-auto-2 cih-auto-1";
+  checkSize(100, 75, "Using last remembered sizes from different times");
+}, "contain:inline-size can keep previous last remembered inline size");
+</script>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
deleted file mode 100644
index 26f04d46..0000000
--- a/third_party/blink/web_tests/platform/linux/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-PASS .container > div 2
-PASS .container > div 3
-PASS .container > div 4
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-PASS .container > div 9
-PASS .container > div 10
-PASS .container > div 11
-PASS .container > div 12
-PASS .container > div 13
-PASS .container > div 14
-PASS .container > div 15
-PASS .container > div 16
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-PASS .container > div 21
-PASS .container > div 22
-PASS .container > div 23
-PASS .container > div 24
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
deleted file mode 100644
index 26f04d46..0000000
--- a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-PASS .container > div 2
-PASS .container > div 3
-PASS .container > div 4
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-PASS .container > div 9
-PASS .container > div 10
-PASS .container > div 11
-PASS .container > div 12
-PASS .container > div 13
-PASS .container > div 14
-PASS .container > div 15
-PASS .container > div 16
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-PASS .container > div 21
-PASS .container > div 22
-PASS .container > div 23
-PASS .container > div 24
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt b/third_party/blink/web_tests/platform/win10/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
deleted file mode 100644
index 26f04d46..0000000
--- a/third_party/blink/web_tests/platform/win10/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-PASS .container > div 2
-PASS .container > div 3
-PASS .container > div 4
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-PASS .container > div 9
-PASS .container > div 10
-PASS .container > div 11
-PASS .container > div 12
-PASS .container > div 13
-PASS .container > div 14
-PASS .container > div 15
-PASS .container > div 16
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-PASS .container > div 21
-PASS .container > div 22
-PASS .container > div 23
-PASS .container > div 24
-Harness: the test ran to completion.
-
diff --git a/third_party/tflite_support/README.chromium b/third_party/tflite_support/README.chromium
index c2aa39c..29bec4d 100644
--- a/third_party/tflite_support/README.chromium
+++ b/third_party/tflite_support/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite Support
 Short Name: tflite-support
 URL: https://github.com/tensorflow/tflite-support
-Version: e9c16a6c1c6e18f9676129108d350edcd00ecca5
-Date: 2022/08/30
+Version: 917725b25ebb95edff71716612d3c56abeddd703
+Date: 2022/09/21
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
@@ -28,15 +28,15 @@
 04) Do not use absl::any since it is not supported in chromium
 05) Remove an unneeded static initializer.
 06) Check (instead of resetting) the cancel_flag_ before Invoking the model.
-07) Remove support for creating a model handler from a file.
-08) Fix minizip path inclusion. Upstream uses contrib/minizip/, but chromium
+07) Fix minizip path inclusion. Upstream uses contrib/minizip/, but chromium
 uses third_party/zlib/contrib/minizip/.
-09) Fix unused variable in task_utils.h.
-10) Run clang-format.
+08) Fix unused variable in task_utils.h.
+09) Run clang-format.
   * This patch might not apply cleanly, so run `git cl format` and commit the
   changes.
-11) Use SysNSStringToUTF8 to convert NSString.
-12) Set executable bit for common_win.bat.
+10) Use SysNSStringToUTF8 to convert NSString.
+11) Set executable bit for common_win.bat.
+12) Fix unused function/field errors on windows.
 13) Remove unbuilt files that cause `git cl presubmit` errors.
   * This patch intentionally does not apply because it was made with
   `--irreversible-delete` because it is deleting a large .tflite file causing
diff --git a/third_party/tflite_support/patches/0001-use-re2-StringPiece-for-RegexTokenizer-Tokenize.patch b/third_party/tflite_support/patches/0001-use-re2-StringPiece-for-RegexTokenizer-Tokenize.patch
index a004574..72d9e252 100644
--- a/third_party/tflite_support/patches/0001-use-re2-StringPiece-for-RegexTokenizer-Tokenize.patch
+++ b/third_party/tflite_support/patches/0001-use-re2-StringPiece-for-RegexTokenizer-Tokenize.patch
@@ -1,4 +1,4 @@
-From 75beb78f7fd5f832fefdd9d7de003573d3c5da4f Mon Sep 17 00:00:00 2001
+From 097ca97449e4c33e6db6bb44fbbb5e75e572ccb2 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 17:57:43 +0000
 Subject: [PATCH 01/12] use re2 StringPiece for RegexTokenizer Tokenize
@@ -32,5 +32,5 @@
      bool has_non_empty_token = token.length() > 0;
  
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0002-sentencepiece-tokenization-not-supported.patch b/third_party/tflite_support/patches/0002-sentencepiece-tokenization-not-supported.patch
index 774284c..33e6263 100644
--- a/third_party/tflite_support/patches/0002-sentencepiece-tokenization-not-supported.patch
+++ b/third_party/tflite_support/patches/0002-sentencepiece-tokenization-not-supported.patch
@@ -1,4 +1,4 @@
-From 4cbdd8fec5940872e54a7090d75f1890fd6ca6bb Mon Sep 17 00:00:00 2001
+From be0c241f6a8e3ee5330795ce1c5bc23b9da936b6 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 17:58:53 +0000
 Subject: [PATCH 02/12] sentencepiece tokenization not supported
@@ -47,5 +47,5 @@
      case ProcessUnitOptions_RegexTokenizerOptions: {
        const tflite::RegexTokenizerOptions* options =
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0003-rm-noop-deprecated-attribute.patch b/third_party/tflite_support/patches/0003-rm-noop-deprecated-attribute.patch
index 3ba174d..3ababb73 100644
--- a/third_party/tflite_support/patches/0003-rm-noop-deprecated-attribute.patch
+++ b/third_party/tflite_support/patches/0003-rm-noop-deprecated-attribute.patch
@@ -1,4 +1,4 @@
-From 3cd89d0270d139d142f678dfd1d9724d1c02da7e Mon Sep 17 00:00:00 2001
+From e070133fa532c05ca05a63ac870582397e3d1525 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 17:59:47 +0000
 Subject: [PATCH 03/12] rm noop deprecated attribute
@@ -22,5 +22,5 @@
    int input_tensor_index = 0;
    int output_score_tensor_index = 0;
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0004-do-not-use-absl-any.patch b/third_party/tflite_support/patches/0004-do-not-use-absl-any.patch
index 0ae4c4b..4b8c5922 100644
--- a/third_party/tflite_support/patches/0004-do-not-use-absl-any.patch
+++ b/third_party/tflite_support/patches/0004-do-not-use-absl-any.patch
@@ -1,4 +1,4 @@
-From fc97a636ba1314027fed742e7e956cad8c354e8e Mon Sep 17 00:00:00 2001
+From 65b5803e7240b03fa553ee800d49d947ff9d8162 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:01:41 +0000
 Subject: [PATCH 04/12] do not use absl any
@@ -60,5 +60,5 @@
    Format format_;
    Orientation orientation_;
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0005-rm-stdio-static-init.patch b/third_party/tflite_support/patches/0005-rm-stdio-static-init.patch
index 886fefb..7c93194 100644
--- a/third_party/tflite_support/patches/0005-rm-stdio-static-init.patch
+++ b/third_party/tflite_support/patches/0005-rm-stdio-static-init.patch
@@ -1,4 +1,4 @@
-From 1a9023d1663ffd47530a88456ecf6314ad6a3318 Mon Sep 17 00:00:00 2001
+From 3da4e72ce30417d9c86f4b24907c7dfc1bce3801 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:03:01 +0000
 Subject: [PATCH 05/12] rm stdio static init
@@ -34,5 +34,5 @@
  using ::tflite::proto::ComputeSettings;
  using ::tflite::support::CreateStatusWithPayload;
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0006-check-cancel-flag-before-calling-invoke.patch b/third_party/tflite_support/patches/0006-check-cancel-flag-before-calling-invoke.patch
index 4b518f4..0f1b9e1 100644
--- a/third_party/tflite_support/patches/0006-check-cancel-flag-before-calling-invoke.patch
+++ b/third_party/tflite_support/patches/0006-check-cancel-flag-before-calling-invoke.patch
@@ -1,4 +1,4 @@
-From df0f9a06629682297f4cadd96df0abbebd174feb Mon Sep 17 00:00:00 2001
+From ea6b08771f2c34fa9d57e7ad5520cccfaa4eb194 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:03:52 +0000
 Subject: [PATCH 06/12] check cancel flag before calling invoke
@@ -53,5 +53,5 @@
      }
      return absl::InternalError("Invoke() failed.");
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0008-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch b/third_party/tflite_support/patches/0007-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch
similarity index 96%
rename from third_party/tflite_support/patches/0008-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch
rename to third_party/tflite_support/patches/0007-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch
index 3955794..c8499d3 100644
--- a/third_party/tflite_support/patches/0008-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch
+++ b/third_party/tflite_support/patches/0007-Fix-TFLite-build-errors-on-linux-when-using-the-syst.patch
@@ -1,7 +1,7 @@
-From de908b777dc9d52e9c6b88c582ecf906cf645760 Mon Sep 17 00:00:00 2001
+From 16c2e92b6c01382d368f54e55c565de9c0477a16 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:38:00 +0000
-Subject: [PATCH 08/12] Fix TFLite build errors on linux when using the system
+Subject: [PATCH 07/12] Fix TFLite build errors on linux when using the system
  zlib
 
 ---
@@ -94,5 +94,5 @@
  namespace tflite {
  namespace metadata {
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0007-remove-support-for-creating-a-model-handler-from-a-f.patch b/third_party/tflite_support/patches/0007-remove-support-for-creating-a-model-handler-from-a-f.patch
deleted file mode 100644
index bdb62ab1..0000000
--- a/third_party/tflite_support/patches/0007-remove-support-for-creating-a-model-handler-from-a-f.patch
+++ /dev/null
@@ -1,243 +0,0 @@
-From 08854a00c145a84d50e010fc6a949fd4d47c3acb Mon Sep 17 00:00:00 2001
-From: Jun Zou <junzou@chromium.org>
-Date: Tue, 30 Aug 2022 18:12:00 +0000
-Subject: [PATCH 07/12] remove support for creating a model handler from a file
-
----
- .../cc/task/core/external_file_handler.cc     | 136 +-----------------
- .../cc/task/core/external_file_handler.h      |  21 ---
- .../cc/task/core/tflite_engine.cc             |   4 -
- .../cc/task/core/tflite_engine.h              |   4 -
- 4 files changed, 6 insertions(+), 159 deletions(-)
-
-diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
-index 5e17e14dc5f7a..09d29f7b83eb7 100644
---- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
-+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
-@@ -15,12 +15,6 @@ limitations under the License.
- 
- #include "tensorflow_lite_support/cc/task/core/external_file_handler.h"
- 
--#include <errno.h>
--#include <fcntl.h>
--#include <stddef.h>
--#include <sys/mman.h>
--#include <unistd.h>
--
- #include <memory>
- #include <string>
- 
-@@ -40,18 +34,6 @@ using ::tflite::support::CreateStatusWithPayload;
- using ::tflite::support::StatusOr;
- using ::tflite::support::TfLiteSupportStatus;
- 
--// Gets the offset aligned to page size for mapping given files into memory by
--// file descriptor correctly, as according to mmap(2), the offset used in mmap
--// must be a multiple of sysconf(_SC_PAGE_SIZE).
--int64 GetPageSizeAlignedOffset(int64 offset) {
--  int64 aligned_offset = offset;
--  int64 page_size = sysconf(_SC_PAGE_SIZE);
--  if (offset % page_size != 0) {
--    aligned_offset = offset / page_size * page_size;
--  }
--  return aligned_offset;
--}
--
- }  // namespace
- 
- /* static */
-@@ -71,123 +53,17 @@ absl::Status ExternalFileHandler::MapExternalFile() {
-   if (!external_file_.file_content().empty()) {
-     return absl::OkStatus();
-   }
--  if (external_file_.file_name().empty() &&
--      !external_file_.has_file_descriptor_meta()) {
--    return CreateStatusWithPayload(
--        StatusCode::kInvalidArgument,
--        "ExternalFile must specify at least one of 'file_content', 'file_name' "
--        "or 'file_descriptor_meta'.",
--        TfLiteSupportStatus::kInvalidArgumentError);
--  }
--  // Obtain file descriptor, offset and size.
--  int fd = -1;
--  if (!external_file_.file_name().empty()) {
--    owned_fd_ = open(external_file_.file_name().c_str(), O_RDONLY);
--    if (owned_fd_ < 0) {
--      const std::string error_message = absl::StrFormat(
--          "Unable to open file at %s", external_file_.file_name());
--      switch (errno) {
--        case ENOENT:
--          return CreateStatusWithPayload(
--              StatusCode::kNotFound, error_message,
--              TfLiteSupportStatus::kFileNotFoundError);
--        case EACCES:
--        case EPERM:
--          return CreateStatusWithPayload(
--              StatusCode::kPermissionDenied, error_message,
--              TfLiteSupportStatus::kFilePermissionDeniedError);
--        case EINTR:
--          return CreateStatusWithPayload(StatusCode::kUnavailable,
--                                         error_message,
--                                         TfLiteSupportStatus::kFileReadError);
--        case EBADF:
--          return CreateStatusWithPayload(StatusCode::kFailedPrecondition,
--                                         error_message,
--                                         TfLiteSupportStatus::kFileReadError);
--        default:
--          return CreateStatusWithPayload(
--              StatusCode::kUnknown,
--              absl::StrFormat("%s, errno=%d", error_message, errno),
--              TfLiteSupportStatus::kFileReadError);
--      }
--    }
--    fd = owned_fd_;
--  } else {
--    fd = external_file_.file_descriptor_meta().fd();
--    if (fd < 0) {
--      return CreateStatusWithPayload(
--          StatusCode::kInvalidArgument,
--          absl::StrFormat("Provided file descriptor is invalid: %d < 0", fd),
--          TfLiteSupportStatus::kInvalidArgumentError);
--    }
--    buffer_offset_ = external_file_.file_descriptor_meta().offset();
--    buffer_size_ = external_file_.file_descriptor_meta().length();
--  }
--  // Get actual file size. Always use 0 as offset to lseek(2) to get the actual
--  // file size, as SEEK_END returns the size of the file *plus* offset.
--  size_t file_size = lseek(fd, /*offset=*/0, SEEK_END);
--  if (file_size <= 0) {
--    return CreateStatusWithPayload(
--        StatusCode::kUnknown,
--        absl::StrFormat("Unable to get file size, errno=%d", errno),
--        TfLiteSupportStatus::kFileReadError);
--  }
--  // Deduce buffer size if not explicitly provided through file descriptor.
--  if (buffer_size_ <= 0) {
--    buffer_size_ = file_size - buffer_offset_;
--  }
--  // Check for out of range issues.
--  if (file_size <= buffer_offset_) {
--    return CreateStatusWithPayload(
--        StatusCode::kInvalidArgument,
--        absl::StrFormat("Provided file offset (%d) exceeds or matches actual "
--                        "file length (%d)",
--                        buffer_offset_, file_size),
--        TfLiteSupportStatus::kInvalidArgumentError);
--  }
--  if (file_size < buffer_size_ + buffer_offset_) {
--    return CreateStatusWithPayload(
--        StatusCode::kInvalidArgument,
--        absl::StrFormat("Provided file length + offset (%d) exceeds actual "
--                        "file length (%d)",
--                        buffer_size_ + buffer_offset_, file_size),
--        TfLiteSupportStatus::kInvalidArgumentError);
--  }
--  // If buffer_offset_ is not multiple of sysconf(_SC_PAGE_SIZE), align with
--  // extra leading bytes and adjust buffer_size_ to account for the extra
--  // leading bytes.
--  buffer_aligned_offset_ = GetPageSizeAlignedOffset(buffer_offset_);
--  buffer_aligned_size_ = buffer_size_ + buffer_offset_ - buffer_aligned_offset_;
--  // Map into memory.
--  buffer_ = mmap(/*addr=*/nullptr, buffer_aligned_size_, PROT_READ, MAP_SHARED,
--                 fd, buffer_aligned_offset_);
--  if (buffer_ == MAP_FAILED) {
--    return CreateStatusWithPayload(
--        StatusCode::kUnknown,
--        absl::StrFormat("Unable to map file to memory buffer, errno=%d", errno),
--        TfLiteSupportStatus::kFileMmapError);
--  }
--  return absl::OkStatus();
-+  return CreateStatusWithPayload(
-+      StatusCode::kInvalidArgument,
-+      "ExternalFile must specify 'file_content' in Chromium.",
-+      TfLiteSupportStatus::kInvalidArgumentError);
- }
- 
- absl::string_view ExternalFileHandler::GetFileContent() {
--  if (!external_file_.file_content().empty()) {
--    return external_file_.file_content();
--  } else {
--    return absl::string_view(static_cast<const char*>(buffer_) +
--                                 buffer_offset_ - buffer_aligned_offset_,
--                             buffer_size_);
--  }
-+  return external_file_.file_content();
- }
- 
--ExternalFileHandler::~ExternalFileHandler() {
--  if (buffer_ != MAP_FAILED) {
--    munmap(buffer_, buffer_aligned_size_);
--  }
--  if (owned_fd_ >= 0) {
--    close(owned_fd_);
--  }
--}
-+ExternalFileHandler::~ExternalFileHandler() = default;
- 
- }  // namespace core
- }  // namespace task
-diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
-index e8b6831c6ad69..08c3111b4b0ec 100644
---- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
-+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
-@@ -64,27 +64,6 @@ class ExternalFileHandler {
- 
-   // Reference to the input ExternalFile.
-   const ExternalFile& external_file_;
--
--  // The file descriptor of the ExternalFile if provided by path, as it is
--  // opened and owned by this class. Set to -1 otherwise.
--  int owned_fd_{-1};
--
--  // Points to the memory buffer mapped from the file descriptor of the
--  // ExternalFile, if provided by path or file descriptor.
--  void* buffer_{};
--
--  // The mapped memory buffer offset, if any.
--  int64 buffer_offset_{};
--  // The size in bytes of the mapped memory buffer, if any.
--  int64 buffer_size_{};
--
--  // As mmap(2) requires the offset to be a multiple of sysconf(_SC_PAGE_SIZE):
--
--  // The aligned mapped memory buffer offset, if any.
--  int64 buffer_aligned_offset_{};
--  // The aligned mapped memory buffer size in bytes taking into account the
--  // offset shift introduced by buffer_aligned_memory_offset_, if any.
--  int64 buffer_aligned_size_{};
- };
- 
- }  // namespace core
-diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
-index 0ec88d5bd1846..5999090cab973 100644
---- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
-+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
-@@ -15,10 +15,6 @@ limitations under the License.
- 
- #include "tensorflow_lite_support/cc/task/core/tflite_engine.h"
- 
--#if defined(OS_POSIX) || defined(OS_FUCHSIA)
--#include <unistd.h>
--#endif
--
- #include <memory>
- 
- #include "absl/strings/match.h"  // from @com_google_absl
-diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
-index e4eed604d676e..53dabdc4841d7 100644
---- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
-+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
-@@ -33,10 +33,6 @@ limitations under the License.
- #include "tensorflow_lite_support/cc/task/core/proto/external_file_proto_inc.h"
- #include "tensorflow_lite_support/metadata/cc/metadata_extractor.h"
- 
--#ifdef ABSL_HAVE_MMAP
--#include <sys/mman.h>
--#endif
--
- namespace tflite {
- namespace task {
- namespace core {
--- 
-2.37.2.672.g94769d06f0-goog
-
diff --git a/third_party/tflite_support/patches/0009-fix-unused-variable-in-task-utils.patch b/third_party/tflite_support/patches/0008-fix-unused-variable-in-task-utils.patch
similarity index 88%
rename from third_party/tflite_support/patches/0009-fix-unused-variable-in-task-utils.patch
rename to third_party/tflite_support/patches/0008-fix-unused-variable-in-task-utils.patch
index 0462090..e8b443c 100644
--- a/third_party/tflite_support/patches/0009-fix-unused-variable-in-task-utils.patch
+++ b/third_party/tflite_support/patches/0008-fix-unused-variable-in-task-utils.patch
@@ -1,7 +1,7 @@
-From 00f1725ce2025ad86b6acc4b71c3d8df081b9334 Mon Sep 17 00:00:00 2001
+From 4486076d02e6fbb5feb60a5b7e28d3d3c7ce7186 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:42:25 +0000
-Subject: [PATCH 09/12] fix unused variable in task utils
+Subject: [PATCH 08/12] fix unused variable in task utils
 
 ---
  .../src/tensorflow_lite_support/cc/task/core/task_utils.h   | 6 ++++--
@@ -25,5 +25,5 @@
    data->reserve(num);
    for (int i = 0; i < num; i++) {
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0010-run-clang-format.patch b/third_party/tflite_support/patches/0009-run-clang-format.patch
similarity index 99%
rename from third_party/tflite_support/patches/0010-run-clang-format.patch
rename to third_party/tflite_support/patches/0009-run-clang-format.patch
index f8d00171..7a207ab 100644
--- a/third_party/tflite_support/patches/0010-run-clang-format.patch
+++ b/third_party/tflite_support/patches/0009-run-clang-format.patch
@@ -1,7 +1,7 @@
-From 317f40c5cb75655e87f47980d7262212803a0788 Mon Sep 17 00:00:00 2001
-From: Jun Zou <junzou@chromium.org>
-Date: Tue, 30 Aug 2022 18:43:05 +0000
-Subject: [PATCH 10/12] run clang-format
+From 64fda4f0a2700aeed5ef3b8e273f28c24f84b805 Mon Sep 17 00:00:00 2001
+From: Clark DuVall <cduvall@chromium.org>
+Date: Wed, 21 Sep 2022 10:44:20 -0700
+Subject: [PATCH 09/12] run clang format
 
 ---
  .../configuration/edgetpu_coral_plugin.cc     |   20 +-
@@ -27,7 +27,6 @@
  .../c/task/vision/image_segmenter.h           |    6 +-
  .../c/task/vision/object_detector.cc          |    6 +-
  .../c/task/vision/object_detector.h           |    6 +-
- .../test/task/audio/audio_classifier_test.cc  |   36 +-
  .../test/task/vision/image_classifier_test.cc |   84 +-
  .../test/task/vision/image_segmenter_test.cc  |   62 +-
  .../test/task/vision/object_detector_test.cc  |   90 +-
@@ -49,7 +48,7 @@
  .../cc/task/core/base_task_api.h              |    2 +-
  .../cc/task/core/classification_head.h        |    2 +-
  .../cc/task/core/error_reporter.cc            |    8 +-
- .../cc/task/core/external_file_handler.cc     |    4 +-
+ .../cc/task/core/external_file_handler.cc     |   13 +-
  .../cc/task/core/external_file_handler.h      |    2 +-
  .../cc/task/core/label_map_item.cc            |    5 +-
  .../cc/task/core/label_map_item.h             |    7 +-
@@ -463,7 +462,7 @@
  .../leveldb_testing_utils_py_wrapper.cc       |   14 +-
  .../src/third_party/fft2d/fft.h               |   12 +-
  .../src/third_party/fft2d/fft2d.h             |   12 +-
- 459 files changed, 20471 insertions(+), 19530 deletions(-)
+ 458 files changed, 20455 insertions(+), 19519 deletions(-)
 
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/acceleration/configuration/edgetpu_coral_plugin.cc b/third_party/tflite_support/src/tensorflow_lite_support/acceleration/configuration/edgetpu_coral_plugin.cc
 index 9f27f3baae82f..6a16d12856258 100644
@@ -992,103 +991,6 @@
      TfLiteSupportError** error);
  
  // Disposes off the object detector.
-diff --git a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc
-index 948b64288d40f..c9bae179fb8d9 100644
---- a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc
-+++ b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc
-@@ -45,9 +45,10 @@ constexpr char kYamNetAudioClassifierWithMetadata[] =
-     "yamnet_audio_classifier_with_metadata.tflite";
- 
- StatusOr<TfLiteAudioBuffer> LoadAudioBufferFromFileNamed(
--    const std::string wav_file, int buffer_size) {
--  std::string contents = ReadFile(
--      JoinPath("./" /*test src dir*/, kTestDataDirectory, wav_file));
-+    const std::string wav_file,
-+    int buffer_size) {
-+  std::string contents =
-+      ReadFile(JoinPath("./" /*test src dir*/, kTestDataDirectory, wav_file));
- 
-   uint32_t offset = 0;
-   uint32_t decoded_sample_count;
-@@ -91,7 +92,8 @@ void Verify(TfLiteClassificationResult* classification_result,
- }
- 
- void Verify(TfLiteClassifications& classifications,
--            int expected_categories_size, int expected_head_index,
-+            int expected_categories_size,
-+            int expected_head_index,
-             char const* expected_head_name) {
-   EXPECT_EQ(classifications.size, expected_categories_size);
-   EXPECT_EQ(classifications.head_index, expected_head_index);
-@@ -102,8 +104,10 @@ void Verify(TfLiteClassifications& classifications,
-   EXPECT_NE(classifications.categories, nullptr);
- }
- 
--void Verify(TfLiteCategory& category, int expected_index,
--            char const* expected_label, float expected_score) {
-+void Verify(TfLiteCategory& category,
-+            int expected_index,
-+            char const* expected_label,
-+            float expected_score) {
-   const float kPrecision = 1e-6;
-   EXPECT_EQ(category.index, expected_index);
-   EXPECT_NE(category.label, nullptr);
-@@ -116,7 +120,8 @@ void Verify(TfLiteCategory& category, int expected_index,
-   EXPECT_NEAR(category.score, expected_score, kPrecision);
- }
- 
--void Verify(TfLiteSupportError* error, TfLiteSupportErrorCode error_code,
-+void Verify(TfLiteSupportError* error,
-+            TfLiteSupportErrorCode error_code,
-             char const* message) {
-   ASSERT_NE(error, nullptr);
-   EXPECT_EQ(error->code, kInvalidArgumentError);
-@@ -134,7 +139,8 @@ TEST_F(AudioClassifierFromOptionsTest, FailsWithMissingModelPathAndError) {
-       TfLiteAudioClassifierFromOptions(&options, &error);
- 
-   EXPECT_EQ(audio_classifier, nullptr);
--  if (audio_classifier) TfLiteAudioClassifierDelete(audio_classifier);
-+  if (audio_classifier)
-+    TfLiteAudioClassifierDelete(audio_classifier);
- 
-   Verify(error, kInvalidArgumentError,
-          "INVALID_ARGUMENT: Missing mandatory `model_file` field in "
-@@ -144,9 +150,8 @@ TEST_F(AudioClassifierFromOptionsTest, FailsWithMissingModelPathAndError) {
- }
- 
- TEST_F(AudioClassifierFromOptionsTest, SucceedsWithModelPath) {
--  std::string model_path =
--      JoinPath("./" /*test src dir*/, kTestDataDirectory,
--               kYamNetAudioClassifierWithMetadata);
-+  std::string model_path = JoinPath("./" /*test src dir*/, kTestDataDirectory,
-+                                    kYamNetAudioClassifierWithMetadata);
-   TfLiteAudioClassifierOptions options = TfLiteAudioClassifierOptionsCreate();
-   options.base_options.model_file.file_path = model_path.data();
-   TfLiteAudioClassifier* audio_classifier =
-@@ -159,9 +164,8 @@ TEST_F(AudioClassifierFromOptionsTest, SucceedsWithModelPath) {
- class AudioClassifierClassifyTest : public tflite_shims::testing::Test {
-  protected:
-   void SetUp() override {
--    std::string model_path =
--        JoinPath("./" /*test src dir*/, kTestDataDirectory,
--                 kYamNetAudioClassifierWithMetadata);
-+    std::string model_path = JoinPath("./" /*test src dir*/, kTestDataDirectory,
-+                                      kYamNetAudioClassifierWithMetadata);
- 
-     TfLiteAudioClassifierOptions options = TfLiteAudioClassifierOptionsCreate();
-     options.base_options.model_file.file_path = model_path.data();
-@@ -194,9 +198,9 @@ TEST_F(AudioClassifierClassifyTest, SucceedsWithAudioFile) {
-   Verify(classification_result->classifications[0].categories[0], 0, "Speech",
-          0.917969);
-   Verify(classification_result->classifications[0].categories[1], 500,
--           "Inside, small room", 0.058594);
-+         "Inside, small room", 0.058594);
-   Verify(classification_result->classifications[0].categories[2], 494,
--           "Silence", 0.011719);
-+         "Silence", 0.011719);
-   TfLiteClassificationResultDelete(classification_result);
- }
- 
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/vision/image_classifier_test.cc b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/vision/image_classifier_test.cc
 index 0a59344f4394c..cce2fa63fad17 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/vision/image_classifier_test.cc
@@ -2240,10 +2142,21 @@
  }  // namespace core
  }  // namespace task
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
-index 09d29f7b83eb7..e15830d5ab061 100644
+index 04fbbd3d00bc0..7aed2f10ed87a 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
 +++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
-@@ -18,11 +18,11 @@ limitations under the License.
+@@ -24,9 +24,9 @@ limitations under the License.
+ #endif
+ 
+ #ifdef _WIN32
+-#include <windows.h>
+ #include <direct.h>
+ #include <io.h>
++#include <windows.h>
+ #else
+ #include <unistd.h>
+ #endif
+@@ -34,11 +34,11 @@ limitations under the License.
  #include <memory>
  #include <string>
  
@@ -2257,8 +2170,22 @@
  
  namespace tflite {
  namespace task {
+@@ -85,10 +85,9 @@ ExternalFileHandler::CreateFromExternalFile(const ExternalFile* external_file) {
+ absl::Status ExternalFileHandler::MapExternalFile() {
+ // TODO(b/195588083): Add Windows support
+ #ifdef _WIN32
+-  return CreateStatusWithPayload(
+-      StatusCode::kFailedPrecondition,
+-      "File loading is not yet supported on Windows",
+-      TfLiteSupportStatus::kFileReadError);
++  return CreateStatusWithPayload(StatusCode::kFailedPrecondition,
++                                 "File loading is not yet supported on Windows",
++                                 TfLiteSupportStatus::kFileReadError);
+ #else
+   if (!external_file_.file_content().empty()) {
+     return absl::OkStatus();
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
-index 08c3111b4b0ec..9f35fdd6d09ce 100644
+index e8b6831c6ad69..0b74e468d004f 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
 +++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
 @@ -18,7 +18,7 @@ limitations under the License.
@@ -2481,10 +2408,10 @@
  
    return FindTensorIndexByModelName(tensors, model_tensor_name);
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
-index 5999090cab973..41e06389af80b 100644
+index 0ec88d5bd1846..1ccd1e1480f51 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
 +++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
-@@ -17,7 +17,7 @@ limitations under the License.
+@@ -21,7 +21,7 @@ limitations under the License.
  
  #include <memory>
  
@@ -2493,7 +2420,7 @@
  #include "absl/strings/str_cat.h"  // from @com_google_absl
  #include "tensorflow/lite/builtin_ops.h"
  #include "tensorflow/lite/core/shims/cc/kernels/register.h"
-@@ -38,7 +38,8 @@ using ::tflite::support::CreateStatusWithPayload;
+@@ -42,7 +42,8 @@ using ::tflite::support::CreateStatusWithPayload;
  using ::tflite::support::InterpreterCreationResources;
  using ::tflite::support::TfLiteSupportStatus;
  
@@ -2503,7 +2430,7 @@
                                      tflite::ErrorReporter* reporter) {
    return tflite_shims::Verify(data, length, reporter);
  }
-@@ -69,7 +70,8 @@ std::vector<const TfLiteTensor*> TfLiteEngine::GetOutputs() {
+@@ -73,7 +74,8 @@ std::vector<const TfLiteTensor*> TfLiteEngine::GetOutputs() {
  }
  
  void TfLiteEngine::VerifyAndBuildModelFromBuffer(
@@ -2513,7 +2440,7 @@
      TfLiteVerifier* extra_verifier) {
    model_ = tflite_shims::FlatBufferModel::VerifyAndBuildFromBuffer(
        buffer_data, buffer_size, extra_verifier, &error_reporter_);
-@@ -116,7 +118,8 @@ absl::Status TfLiteEngine::InitializeFromModelFileHandler(
+@@ -120,7 +122,8 @@ absl::Status TfLiteEngine::InitializeFromModelFileHandler(
  }
  
  absl::Status TfLiteEngine::BuildModelFromFlatBuffer(
@@ -2523,7 +2450,7 @@
      const tflite::proto::ComputeSettings& compute_settings) {
    if (model_) {
      return CreateStatusWithPayload(StatusCode::kInternal,
-@@ -205,7 +208,8 @@ absl::Status TfLiteEngine::InitInterpreter(int num_threads) {
+@@ -209,7 +212,8 @@ absl::Status TfLiteEngine::InitInterpreter(int num_threads) {
  // absl::Status TfLiteEngine::InitInterpreter(
  //    const tflite::proto::ComputeSettings& compute_settings)
  absl::Status TfLiteEngine::InitInterpreter(
@@ -2534,7 +2461,7 @@
    settings_copy.mutable_tflite_settings()
        ->mutable_cpu_settings()
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
-index 53dabdc4841d7..0cbaa738e6db6 100644
+index e4eed604d676e..840b7ccbda201 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
 +++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
 @@ -18,8 +18,8 @@ limitations under the License.
@@ -2548,7 +2475,7 @@
  #include "absl/strings/string_view.h"  // from @com_google_absl
  #include "tensorflow/lite/core/api/op_resolver.h"
  #include "tensorflow/lite/core/shims/c/common.h"
-@@ -96,7 +96,8 @@ class TfLiteEngine {
+@@ -100,7 +100,8 @@ class TfLiteEngine {
    // object. This performs extra verification on the input data using
    // tflite::Verify.
    absl::Status BuildModelFromFlatBuffer(
@@ -2558,7 +2485,7 @@
        const tflite::proto::ComputeSettings& compute_settings =
            tflite::proto::ComputeSettings());
  
-@@ -138,7 +139,8 @@ class TfLiteEngine {
+@@ -142,7 +143,8 @@ class TfLiteEngine {
    // absl::Status TfLiteEngine::InitInterpreter(
    //    const tflite::proto::ComputeSettings& compute_settings)
    absl::Status InitInterpreter(
@@ -2568,7 +2495,7 @@
  
    // Cancels the on-going `Invoke()` call if any and if possible. This method
    // can be called from a different thread than the one where `Invoke()` is
-@@ -155,7 +157,8 @@ class TfLiteEngine {
+@@ -159,7 +161,8 @@ class TfLiteEngine {
    // the FlatBuffer data provided as input.
    class Verifier : public tflite::TfLiteVerifier {
     public:
@@ -8551,7 +8478,7 @@
    return nullptr;
  }
 diff --git a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
-index 41fc5aa28bf30..68f8e64492394 100644
+index b4418f61a6798..bd8e39dc6f3ab 100644
 --- a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
 +++ b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
 @@ -16,16 +16,16 @@ limitations under the License.
@@ -53992,5 +53919,5 @@
  #ifdef __cplusplus
  }
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0011-Use-SysNSStringToUTF8-to-convert-NSString.patch b/third_party/tflite_support/patches/0010-Use-SysNSStringToUTF8-to-convert-NSString.patch
similarity index 85%
rename from third_party/tflite_support/patches/0011-Use-SysNSStringToUTF8-to-convert-NSString.patch
rename to third_party/tflite_support/patches/0010-Use-SysNSStringToUTF8-to-convert-NSString.patch
index 898096e..4e867c9 100644
--- a/third_party/tflite_support/patches/0011-Use-SysNSStringToUTF8-to-convert-NSString.patch
+++ b/third_party/tflite_support/patches/0010-Use-SysNSStringToUTF8-to-convert-NSString.patch
@@ -1,7 +1,7 @@
-From 9f522c3171a63f2b244632ec90646755998129f4 Mon Sep 17 00:00:00 2001
+From 4b1f31cd8bbe84d121017b680b872d513ce162a8 Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:46:22 +0000
-Subject: [PATCH 11/12] Use SysNSStringToUTF8 to convert NSString
+Subject: [PATCH 10/12] Use SysNSStringToUTF8 to convert NSString
 
 ---
  .../tensorflow_lite_support/ios/utils/Sources/TFLStringUtil.mm  | 2 +-
@@ -21,5 +21,5 @@
  
  NSString* MakeNSString(const std::string& str) {
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0012-set-executable-bit-for-common_win.bat.patch b/third_party/tflite_support/patches/0011-set-executable-bit-for-common_win.bat.patch
similarity index 78%
rename from third_party/tflite_support/patches/0012-set-executable-bit-for-common_win.bat.patch
rename to third_party/tflite_support/patches/0011-set-executable-bit-for-common_win.bat.patch
index 5f3e2a80..374ee83 100644
--- a/third_party/tflite_support/patches/0012-set-executable-bit-for-common_win.bat.patch
+++ b/third_party/tflite_support/patches/0011-set-executable-bit-for-common_win.bat.patch
@@ -1,7 +1,7 @@
-From 287d722ad911cbabe4266f5ca7338f72e0cc75a4 Mon Sep 17 00:00:00 2001
+From c7ab6fe44bb111ec13658f0dde74325d0ef6e37f Mon Sep 17 00:00:00 2001
 From: Jun Zou <junzou@chromium.org>
 Date: Tue, 30 Aug 2022 18:47:38 +0000
-Subject: [PATCH 12/12] set executable bit for common_win.bat
+Subject: [PATCH 11/12] set executable bit for common_win.bat
 
 ---
  .../src/tensorflow_lite_support/tools/ci_build/common_win.bat     | 0
@@ -12,5 +12,5 @@
 old mode 100644
 new mode 100755
 -- 
-2.37.2.672.g94769d06f0-goog
+2.37.3.998.g577e59143f-goog
 
diff --git a/third_party/tflite_support/patches/0012-fix-unused-errors-on-win.patch b/third_party/tflite_support/patches/0012-fix-unused-errors-on-win.patch
new file mode 100644
index 0000000..cdbb7cc
--- /dev/null
+++ b/third_party/tflite_support/patches/0012-fix-unused-errors-on-win.patch
@@ -0,0 +1,75 @@
+From 07922e43eaa66d2ebc8ff59db147b67e49d0694d Mon Sep 17 00:00:00 2001
+From: Clark DuVall <cduvall@chromium.org>
+Date: Wed, 21 Sep 2022 13:36:28 -0700
+Subject: [PATCH 12/12] fix unused errors on win
+
+---
+ .../cc/task/core/external_file_handler.cc          | 14 ++++++--------
+ .../cc/task/core/external_file_handler.h           |  2 ++
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
+index 7aed2f10ed87a..e7197665b5e8b 100644
+--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
++++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
+@@ -53,19 +53,17 @@ using ::tflite::support::TfLiteSupportStatus;
+ // Gets the offset aligned to page size for mapping given files into memory by
+ // file descriptor correctly, as according to mmap(2), the offset used in mmap
+ // must be a multiple of sysconf(_SC_PAGE_SIZE).
++// mmap is not used on Windows
++#ifndef _WIN32
+ int64 GetPageSizeAlignedOffset(int64 offset) {
+-#ifdef _WIN32
+-  // mmap is not used on Windows
+-  return -1;
+-#else
+   int64 aligned_offset = offset;
+   int64 page_size = sysconf(_SC_PAGE_SIZE);
+   if (offset % page_size != 0) {
+     aligned_offset = offset / page_size * page_size;
+   }
+   return aligned_offset;
+-#endif
+ }
++#endif
+ 
+ }  // namespace
+ 
+@@ -83,15 +81,15 @@ ExternalFileHandler::CreateFromExternalFile(const ExternalFile* external_file) {
+ }
+ 
+ absl::Status ExternalFileHandler::MapExternalFile() {
++  if (!external_file_.file_content().empty()) {
++    return absl::OkStatus();
++  }
+ // TODO(b/195588083): Add Windows support
+ #ifdef _WIN32
+   return CreateStatusWithPayload(StatusCode::kFailedPrecondition,
+                                  "File loading is not yet supported on Windows",
+                                  TfLiteSupportStatus::kFileReadError);
+ #else
+-  if (!external_file_.file_content().empty()) {
+-    return absl::OkStatus();
+-  }
+   if (external_file_.file_name().empty() &&
+       !external_file_.has_file_descriptor_meta()) {
+     return CreateStatusWithPayload(
+diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
+index 0b74e468d004f..3dbb91909df4e 100644
+--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
++++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
+@@ -82,9 +82,11 @@ class ExternalFileHandler {
+ 
+   // The aligned mapped memory buffer offset, if any.
+   int64 buffer_aligned_offset_{};
++#ifndef _WIN32
+   // The aligned mapped memory buffer size in bytes taking into account the
+   // offset shift introduced by buffer_aligned_memory_offset_, if any.
+   int64 buffer_aligned_size_{};
++#endif
+ };
+ 
+ }  // namespace core
+-- 
+2.37.3.998.g577e59143f-goog
+
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/BUILD
index 6b8e79dd..5c6ff999 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/BUILD
@@ -10,25 +10,4 @@
     licenses = ["notice"],  # Apache 2.0
 )
 
-# bazel test tensorflow_lite_support/c/test/task/audio:audio_classifier_test
-cc_test_with_tflite(
-    name = "audio_classifier_test",
-    srcs = ["audio_classifier_test.cc"],
-    data = [
-        "//tensorflow_lite_support/cc/test/testdata/task/audio:test_audio_clips",
-        "//tensorflow_lite_support/cc/test/testdata/task/audio:test_models",
-    ],
-    tflite_deps = [
-        "//tensorflow_lite_support/c/task/audio:audio_classifier",
-        "@org_tensorflow//tensorflow/lite/core/shims:cc_shims_test_util",
-    ],
-    deps = [
-        "//tensorflow_lite_support/c:common",
-        "//tensorflow_lite_support/c/task/audio/core:audio_buffer",
-        "//tensorflow_lite_support/c/task/processor:classification_result",
-        "//tensorflow_lite_support/cc/port:gtest_main",
-        "//tensorflow_lite_support/cc/port:statusor",
-        "//tensorflow_lite_support/cc/task/audio/utils:wav_io",
-        "//tensorflow_lite_support/cc/test:test_utils",
-    ],
-)
+# TODO(b/244472798): Fix and enable these tests
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc b/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc
deleted file mode 100644
index c9bae17..0000000
--- a/third_party/tflite_support/src/tensorflow_lite_support/c/test/task/audio/audio_classifier_test.cc
+++ /dev/null
@@ -1,210 +0,0 @@
-/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-==============================================================================*/
-
-#include "tensorflow_lite_support/c/task/audio/audio_classifier.h"
-
-#include <string.h>
-
-#include "tensorflow/lite/core/shims/cc/shims_test_util.h"
-#include "tensorflow_lite_support/c/common.h"
-#include "tensorflow_lite_support/c/task/audio/core/audio_buffer.h"
-#include "tensorflow_lite_support/c/task/processor/classification_result.h"
-#include "tensorflow_lite_support/cc/port/gmock.h"
-#include "tensorflow_lite_support/cc/port/gtest.h"
-#include "tensorflow_lite_support/cc/port/status_matchers.h"
-#include "tensorflow_lite_support/cc/port/statusor.h"
-#include "tensorflow_lite_support/cc/task/audio/utils/wav_io.h"
-#include "tensorflow_lite_support/cc/test/test_utils.h"
-
-namespace tflite {
-namespace task {
-namespace audio {
-namespace {
-
-using ::testing::HasSubstr;
-using ::tflite::support::StatusOr;
-using ::tflite::task::JoinPath;
-
-constexpr char kTestDataDirectory[] =
-    "/tensorflow_lite_support/cc/test/testdata/task/"
-    "audio/";
-// Quantized model.
-constexpr char kYamNetAudioClassifierWithMetadata[] =
-    "yamnet_audio_classifier_with_metadata.tflite";
-
-StatusOr<TfLiteAudioBuffer> LoadAudioBufferFromFileNamed(
-    const std::string wav_file,
-    int buffer_size) {
-  std::string contents =
-      ReadFile(JoinPath("./" /*test src dir*/, kTestDataDirectory, wav_file));
-
-  uint32_t offset = 0;
-  uint32_t decoded_sample_count;
-  uint16_t decoded_channel_count;
-  uint32_t decoded_sample_rate;
-  std::vector<float> wav_data;
-
-  absl::Status read_audio_file_status = DecodeLin16WaveAsFloatVector(
-      contents, &wav_data, &offset, &decoded_sample_count,
-      &decoded_channel_count, &decoded_sample_rate);
-
-  if (decoded_sample_count > buffer_size) {
-    decoded_sample_count = buffer_size;
-  }
-
-  if (!read_audio_file_status.ok()) {
-    return read_audio_file_status;
-  }
-
-  float* c_wav_data = (float*)malloc(sizeof(float) * wav_data.size());
-  if (!c_wav_data) {
-    exit(-1);
-  }
-
-  memcpy(c_wav_data, wav_data.data(), sizeof(float) * wav_data.size());
-
-  TfLiteAudioBuffer audio_buffer = {
-      .format = {.channels = decoded_channel_count,
-                 .sample_rate = static_cast<int>(decoded_sample_rate)},
-      .data = c_wav_data,
-      .size = static_cast<int>(decoded_sample_count)};
-
-  return audio_buffer;
-}
-
-void Verify(TfLiteClassificationResult* classification_result,
-            int expected_classifications_size) {
-  EXPECT_NE(classification_result, nullptr);
-  EXPECT_EQ(classification_result->size, expected_classifications_size);
-  EXPECT_NE(classification_result->classifications, nullptr);
-}
-
-void Verify(TfLiteClassifications& classifications,
-            int expected_categories_size,
-            int expected_head_index,
-            char const* expected_head_name) {
-  EXPECT_EQ(classifications.size, expected_categories_size);
-  EXPECT_EQ(classifications.head_index, expected_head_index);
-  ASSERT_NE(classifications.head_name, nullptr);
-  if (expected_head_name) {
-    EXPECT_EQ(strcmp(classifications.head_name, expected_head_name), 0);
-  }
-  EXPECT_NE(classifications.categories, nullptr);
-}
-
-void Verify(TfLiteCategory& category,
-            int expected_index,
-            char const* expected_label,
-            float expected_score) {
-  const float kPrecision = 1e-6;
-  EXPECT_EQ(category.index, expected_index);
-  EXPECT_NE(category.label, nullptr);
-
-  if (category.label && expected_label) {
-    EXPECT_EQ(strcmp(category.label, expected_label), 0);
-  }
-
-  EXPECT_EQ(category.display_name, nullptr);
-  EXPECT_NEAR(category.score, expected_score, kPrecision);
-}
-
-void Verify(TfLiteSupportError* error,
-            TfLiteSupportErrorCode error_code,
-            char const* message) {
-  ASSERT_NE(error, nullptr);
-  EXPECT_EQ(error->code, kInvalidArgumentError);
-  EXPECT_NE(error->message, nullptr);
-  EXPECT_THAT(error->message, HasSubstr(message));
-}
-
-class AudioClassifierFromOptionsTest : public tflite_shims::testing::Test {};
-
-TEST_F(AudioClassifierFromOptionsTest, FailsWithMissingModelPathAndError) {
-  TfLiteAudioClassifierOptions options = TfLiteAudioClassifierOptionsCreate();
-
-  TfLiteSupportError* error = nullptr;
-  TfLiteAudioClassifier* audio_classifier =
-      TfLiteAudioClassifierFromOptions(&options, &error);
-
-  EXPECT_EQ(audio_classifier, nullptr);
-  if (audio_classifier)
-    TfLiteAudioClassifierDelete(audio_classifier);
-
-  Verify(error, kInvalidArgumentError,
-         "INVALID_ARGUMENT: Missing mandatory `model_file` field in "
-         "`base_options`");
-
-  TfLiteSupportErrorDelete(error);
-}
-
-TEST_F(AudioClassifierFromOptionsTest, SucceedsWithModelPath) {
-  std::string model_path = JoinPath("./" /*test src dir*/, kTestDataDirectory,
-                                    kYamNetAudioClassifierWithMetadata);
-  TfLiteAudioClassifierOptions options = TfLiteAudioClassifierOptionsCreate();
-  options.base_options.model_file.file_path = model_path.data();
-  TfLiteAudioClassifier* audio_classifier =
-      TfLiteAudioClassifierFromOptions(&options, nullptr);
-
-  EXPECT_NE(audio_classifier, nullptr);
-  TfLiteAudioClassifierDelete(audio_classifier);
-}
-
-class AudioClassifierClassifyTest : public tflite_shims::testing::Test {
- protected:
-  void SetUp() override {
-    std::string model_path = JoinPath("./" /*test src dir*/, kTestDataDirectory,
-                                      kYamNetAudioClassifierWithMetadata);
-
-    TfLiteAudioClassifierOptions options = TfLiteAudioClassifierOptionsCreate();
-    options.base_options.model_file.file_path = model_path.data();
-    audio_classifier = TfLiteAudioClassifierFromOptions(&options, nullptr);
-    ASSERT_NE(audio_classifier, nullptr);
-  }
-
-  void TearDown() override { TfLiteAudioClassifierDelete(audio_classifier); }
-  TfLiteAudioClassifier* audio_classifier;
-};
-
-TEST_F(AudioClassifierClassifyTest, SucceedsWithAudioFile) {
-  int input_buffer_size = TfLiteAudioClassifierGetRequiredInputBufferSize(
-      audio_classifier, nullptr);
-  ASSERT_NE(input_buffer_size, -1);
-
-  SUPPORT_ASSERT_OK_AND_ASSIGN(
-      TfLiteAudioBuffer audio_buffer,
-      LoadAudioBufferFromFileNamed("speech.wav", input_buffer_size));
-
-  TfLiteSupportError* classifyError = NULL;
-  TfLiteClassificationResult* classification_result =
-      TfLiteAudioClassifierClassify(audio_classifier, &audio_buffer,
-                                    &classifyError);
-
-  TfLiteAudioBufferDeleteData(audio_buffer);
-
-  Verify(classification_result, 1);
-  Verify(classification_result->classifications[0], 521, 0, "scores");
-  Verify(classification_result->classifications[0].categories[0], 0, "Speech",
-         0.917969);
-  Verify(classification_result->classifications[0].categories[1], 500,
-         "Inside, small room", 0.058594);
-  Verify(classification_result->classifications[0].categories[2], 494,
-         "Silence", 0.011719);
-  TfLiteClassificationResultDelete(classification_result);
-}
-
-}  // namespace
-}  // namespace audio
-}  // namespace task
-}  // namespace tflite
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
index e15830d5..e719766 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.cc
@@ -15,6 +15,22 @@
 
 #include "tensorflow_lite_support/cc/task/core/external_file_handler.h"
 
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+#ifdef ABSL_HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#ifdef _WIN32
+#include <direct.h>
+#include <io.h>
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
 #include <memory>
 #include <string>
 
@@ -34,6 +50,21 @@
 using ::tflite::support::StatusOr;
 using ::tflite::support::TfLiteSupportStatus;
 
+// Gets the offset aligned to page size for mapping given files into memory by
+// file descriptor correctly, as according to mmap(2), the offset used in mmap
+// must be a multiple of sysconf(_SC_PAGE_SIZE).
+// mmap is not used on Windows
+#ifndef _WIN32
+int64 GetPageSizeAlignedOffset(int64 offset) {
+  int64 aligned_offset = offset;
+  int64 page_size = sysconf(_SC_PAGE_SIZE);
+  if (offset % page_size != 0) {
+    aligned_offset = offset / page_size * page_size;
+  }
+  return aligned_offset;
+}
+#endif
+
 }  // namespace
 
 /* static */
@@ -53,17 +84,132 @@
   if (!external_file_.file_content().empty()) {
     return absl::OkStatus();
   }
-  return CreateStatusWithPayload(
-      StatusCode::kInvalidArgument,
-      "ExternalFile must specify 'file_content' in Chromium.",
-      TfLiteSupportStatus::kInvalidArgumentError);
+// TODO(b/195588083): Add Windows support
+#ifdef _WIN32
+  return CreateStatusWithPayload(StatusCode::kFailedPrecondition,
+                                 "File loading is not yet supported on Windows",
+                                 TfLiteSupportStatus::kFileReadError);
+#else
+  if (external_file_.file_name().empty() &&
+      !external_file_.has_file_descriptor_meta()) {
+    return CreateStatusWithPayload(
+        StatusCode::kInvalidArgument,
+        "ExternalFile must specify at least one of 'file_content', 'file_name' "
+        "or 'file_descriptor_meta'.",
+        TfLiteSupportStatus::kInvalidArgumentError);
+  }
+  // Obtain file descriptor, offset and size.
+  int fd = -1;
+  if (!external_file_.file_name().empty()) {
+    owned_fd_ = open(external_file_.file_name().c_str(), O_RDONLY);
+    if (owned_fd_ < 0) {
+      const std::string error_message = absl::StrFormat(
+          "Unable to open file at %s", external_file_.file_name());
+      switch (errno) {
+        case ENOENT:
+          return CreateStatusWithPayload(
+              StatusCode::kNotFound, error_message,
+              TfLiteSupportStatus::kFileNotFoundError);
+        case EACCES:
+        case EPERM:
+          return CreateStatusWithPayload(
+              StatusCode::kPermissionDenied, error_message,
+              TfLiteSupportStatus::kFilePermissionDeniedError);
+        case EINTR:
+          return CreateStatusWithPayload(StatusCode::kUnavailable,
+                                         error_message,
+                                         TfLiteSupportStatus::kFileReadError);
+        case EBADF:
+          return CreateStatusWithPayload(StatusCode::kFailedPrecondition,
+                                         error_message,
+                                         TfLiteSupportStatus::kFileReadError);
+        default:
+          return CreateStatusWithPayload(
+              StatusCode::kUnknown,
+              absl::StrFormat("%s, errno=%d", error_message, errno),
+              TfLiteSupportStatus::kFileReadError);
+      }
+    }
+    fd = owned_fd_;
+  } else {
+    fd = external_file_.file_descriptor_meta().fd();
+    if (fd < 0) {
+      return CreateStatusWithPayload(
+          StatusCode::kInvalidArgument,
+          absl::StrFormat("Provided file descriptor is invalid: %d < 0", fd),
+          TfLiteSupportStatus::kInvalidArgumentError);
+    }
+    buffer_offset_ = external_file_.file_descriptor_meta().offset();
+    buffer_size_ = external_file_.file_descriptor_meta().length();
+  }
+  // Get actual file size. Always use 0 as offset to lseek(2) to get the actual
+  // file size, as SEEK_END returns the size of the file *plus* offset.
+  size_t file_size = lseek(fd, /*offset=*/0, SEEK_END);
+  if (file_size <= 0) {
+    return CreateStatusWithPayload(
+        StatusCode::kUnknown,
+        absl::StrFormat("Unable to get file size, errno=%d", errno),
+        TfLiteSupportStatus::kFileReadError);
+  }
+  // Deduce buffer size if not explicitly provided through file descriptor.
+  if (buffer_size_ <= 0) {
+    buffer_size_ = file_size - buffer_offset_;
+  }
+  // Check for out of range issues.
+  if (file_size <= buffer_offset_) {
+    return CreateStatusWithPayload(
+        StatusCode::kInvalidArgument,
+        absl::StrFormat("Provided file offset (%d) exceeds or matches actual "
+                        "file length (%d)",
+                        buffer_offset_, file_size),
+        TfLiteSupportStatus::kInvalidArgumentError);
+  }
+  if (file_size < buffer_size_ + buffer_offset_) {
+    return CreateStatusWithPayload(
+        StatusCode::kInvalidArgument,
+        absl::StrFormat("Provided file length + offset (%d) exceeds actual "
+                        "file length (%d)",
+                        buffer_size_ + buffer_offset_, file_size),
+        TfLiteSupportStatus::kInvalidArgumentError);
+  }
+  // If buffer_offset_ is not multiple of sysconf(_SC_PAGE_SIZE), align with
+  // extra leading bytes and adjust buffer_size_ to account for the extra
+  // leading bytes.
+  buffer_aligned_offset_ = GetPageSizeAlignedOffset(buffer_offset_);
+  buffer_aligned_size_ = buffer_size_ + buffer_offset_ - buffer_aligned_offset_;
+  // Map into memory.
+  buffer_ = mmap(/*addr=*/nullptr, buffer_aligned_size_, PROT_READ, MAP_SHARED,
+                 fd, buffer_aligned_offset_);
+  if (buffer_ == MAP_FAILED) {
+    return CreateStatusWithPayload(
+        StatusCode::kUnknown,
+        absl::StrFormat("Unable to map file to memory buffer, errno=%d", errno),
+        TfLiteSupportStatus::kFileMmapError);
+  }
+  return absl::OkStatus();
+#endif
 }
 
 absl::string_view ExternalFileHandler::GetFileContent() {
-  return external_file_.file_content();
+  if (!external_file_.file_content().empty()) {
+    return external_file_.file_content();
+  } else {
+    return absl::string_view(static_cast<const char*>(buffer_) +
+                                 buffer_offset_ - buffer_aligned_offset_,
+                             buffer_size_);
+  }
 }
 
-ExternalFileHandler::~ExternalFileHandler() = default;
+ExternalFileHandler::~ExternalFileHandler() {
+#ifndef _WIN32
+  if (buffer_ != MAP_FAILED) {
+    munmap(buffer_, buffer_aligned_size_);
+  }
+#endif
+  if (owned_fd_ >= 0) {
+    close(owned_fd_);
+  }
+}
 
 }  // namespace core
 }  // namespace task
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
index 9f35fdd..3dbb9190 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/external_file_handler.h
@@ -64,6 +64,29 @@
 
   // Reference to the input ExternalFile.
   const ExternalFile& external_file_;
+
+  // The file descriptor of the ExternalFile if provided by path, as it is
+  // opened and owned by this class. Set to -1 otherwise.
+  int owned_fd_{-1};
+
+  // Points to the memory buffer mapped from the file descriptor of the
+  // ExternalFile, if provided by path or file descriptor.
+  void* buffer_{};
+
+  // The mapped memory buffer offset, if any.
+  int64 buffer_offset_{};
+  // The size in bytes of the mapped memory buffer, if any.
+  int64 buffer_size_{};
+
+  // As mmap(2) requires the offset to be a multiple of sysconf(_SC_PAGE_SIZE):
+
+  // The aligned mapped memory buffer offset, if any.
+  int64 buffer_aligned_offset_{};
+#ifndef _WIN32
+  // The aligned mapped memory buffer size in bytes taking into account the
+  // offset shift introduced by buffer_aligned_memory_offset_, if any.
+  int64 buffer_aligned_size_{};
+#endif
 };
 
 }  // namespace core
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
index 41e06389..1ccd1e14 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.cc
@@ -15,6 +15,10 @@
 
 #include "tensorflow_lite_support/cc/task/core/tflite_engine.h"
 
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <unistd.h>
+#endif
+
 #include <memory>
 
 #include "absl/strings/match.h"    // from @com_google_absl
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
index 0cbaa73..840b7cc 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/core/tflite_engine.h
@@ -33,6 +33,10 @@
 #include "tensorflow_lite_support/cc/task/core/proto/external_file_proto_inc.h"
 #include "tensorflow_lite_support/metadata/cc/metadata_extractor.h"
 
+#ifdef ABSL_HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
 namespace tflite {
 namespace task {
 namespace core {
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_classifier_options.proto b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_classifier_options.proto
index 50b7682..34e1747 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_classifier_options.proto
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_classifier_options.proto
@@ -24,7 +24,7 @@
 // Options for setting up an ImageClassifier.
 // Next Id: 15
 message ImageClassifierOptions {
-  // Base options for configuring Task library, such as specifying the TfLite
+  // Base options for configuring MediaPipe Tasks, such as specifying the TfLite
   // model file with metadata, accelerator options, etc.
   optional tflite.task.core.BaseOptions base_options = 14;
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_segmenter_options.proto b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_segmenter_options.proto
index 1aad5eb..b0bc582 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_segmenter_options.proto
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/image_segmenter_options.proto
@@ -24,7 +24,7 @@
 // Options for setting up an ImageSegmenter.
 // Next Id: 9
 message ImageSegmenterOptions {
-  // Base options for configuring Task library, such as specifying the TfLite
+  // Base options for configuring MediaPipe Tasks, such as specifying the TfLite
   // model file with metadata, accelerator options, etc.
   optional tflite.task.core.BaseOptions base_options = 8;
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/object_detector_options.proto b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/object_detector_options.proto
index d10198b..0e75684 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/object_detector_options.proto
+++ b/third_party/tflite_support/src/tensorflow_lite_support/cc/task/vision/proto/object_detector_options.proto
@@ -24,7 +24,7 @@
 // Options for setting up an ObjectDetector.
 // Next Id: 10.
 message ObjectDetectorOptions {
-  // Base options for configuring Task library, such as specifying the TfLite
+  // Base options for configuring MediaPipe Tasks, such as specifying the TfLite
   // model file with metadata, accelerator options, etc.
   optional tflite.task.core.BaseOptions base_options = 9;
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_detokenizer_op.cc b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_detokenizer_op.cc
index bd4b5a17..16b4884 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_detokenizer_op.cc
+++ b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_detokenizer_op.cc
@@ -37,7 +37,7 @@
       shape_inference::DimensionHandle dim;
       TF_RETURN_IF_ERROR(c->Subtract(c->NumElements(c->input(2)), 1, &dim));
       c->set_output(0, c->Vector(dim));
-      return Status::OK();
+      return OkStatus();
     });
 
 template <typename Tsplits>
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
index 68f8e644..bd8e39d 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
+++ b/third_party/tflite_support/src/tensorflow_lite_support/custom_ops/kernel/sentencepiece/sentencepiece_tokenizer_op.cc
@@ -56,7 +56,7 @@
       tensorflow::shape_inference::DimensionHandle num_splits;
       TF_RETURN_IF_ERROR(c->Add(c->NumElements(c->input(1)), 1, &num_splits));
       c->set_output(1, c->Vector(num_splits));
-      return tensorflow::Status::OK();
+      return tensorflow::OkStatus();
     });
 
 class TFSentencepieceOp : public tensorflow::OpKernel {
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/ios/task/text/qa/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/ios/task/text/qa/BUILD
index 4b15aed..0eadd0d 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/ios/task/text/qa/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/ios/task/text/qa/BUILD
@@ -4,7 +4,6 @@
 load("@org_tensorflow//tensorflow/lite:special_rules.bzl", "tflite_ios_lab_runner")
 
 package(
-    default_visibility = ["//tensorflow_lite_support:internal"],
     licenses = ["notice"],  # Apache 2.0
 )
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/ios/test/task/vision/image_classifier/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/ios/test/task/vision/image_classifier/BUILD
index b3d0000..31ca060c 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/ios/test/task/vision/image_classifier/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/ios/test/task/vision/image_classifier/BUILD
@@ -79,7 +79,7 @@
 
 ios_unit_test(
     name = "TFLImageClassifierCoreMLDelegateTest",
-    minimum_os_version = "11.0",
+    minimum_os_version = TFL_MINIMUM_OS_VERSION,
     runner = tflite_ios_lab_runner("IOS_LATEST"),
     tags = TFL_DEFAULT_TAGS + TFL_DISABLED_SANITIZER_TAGS,
     deps = [
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/BUILD
index 1769dac2..b557181 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/BUILD
@@ -16,7 +16,6 @@
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/classifier:audio_classifier_src",
     ],
     # TODO(b/163039980): Use JAVACOPTS in TF. "-Xep:RemoveUnusedImports:ERROR" wierdly break the build.
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     visibility = ["//visibility:public"],
     # LINT.IfChange(dep)
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/classifier/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/classifier/BUILD
index d5678f77..1bc45dd 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/classifier/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio/classifier/BUILD
@@ -26,7 +26,6 @@
 android_library(
     name = "audio_classifier_java",
     srcs = [":audio_classifier_src"],
-    javacopts = ["-source 7 -target 7"],
     manifest = "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/audio:AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core/BUILD
index 9a15bbb7..5c2cbd7 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core/BUILD
@@ -10,7 +10,6 @@
     srcs = glob(["**/*.java"]) + [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/processor:task_processor_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     proguard_specs = ["proguard.flags"],
     visibility = ["//visibility:public"],
     # LINT.IfChange(dep)
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/BUILD
index 4a70a51..8b8cf8f 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/BUILD
@@ -16,7 +16,6 @@
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/qa:bert_question_answerer_src",
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/searcher:text_searcher_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     tflite_exports = [
         "//tensorflow_lite_support/java/src/native/task/text:task_text_native",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/bertclu/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/bertclu/BUILD
index 088cfac1..9a8ed60 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/bertclu/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/bertclu/BUILD
@@ -30,7 +30,6 @@
 android_library(
     name = "bert_clu_annotator_java",
     srcs = glob(["*.java"]),
-    javacopts = ["-source 7 -target 7"],
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core:base_task_api",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/nlclassifier/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/nlclassifier/BUILD
index 0079bfda9..0346592 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/nlclassifier/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/nlclassifier/BUILD
@@ -32,7 +32,6 @@
     srcs = [
         "NLClassifier.java",
     ],
-    javacopts = ["-source 7 -target 7"],
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core:base_task_api",
@@ -57,7 +56,6 @@
     srcs = [
         "BertNLClassifier.java",
     ],
-    javacopts = ["-source 7 -target 7"],
     tflite_exports = [
         "//tensorflow_lite_support/java/src/native/task/text/nlclassifier/bert:bert_nl_classifier_native",
     ],
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/qa/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/qa/BUILD
index 0d35eb8..f9190fb5 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/qa/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/qa/BUILD
@@ -30,7 +30,6 @@
 android_library(
     name = "bert_question_answerer_java",
     srcs = glob(["*.java"]),
-    javacopts = ["-source 7 -target 7"],
     deps = [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core:base_task_api",
         "@com_google_auto_value",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/searcher/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/searcher/BUILD
index e4f4981..3ba0631 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/searcher/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/text/searcher/BUILD
@@ -34,7 +34,6 @@
 android_library(
     name = "text_searcher_java",
     srcs = glob(["*.java"]),
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/core:base_task_api",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/BUILD
index 172f6c41..387105b9 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/BUILD
@@ -19,7 +19,6 @@
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/searcher:image_searcher_src",
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/segmenter:image_segmenter_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     tflite_exports = [
         "//tensorflow_lite_support/java/src/native/task/vision:task_vision_native",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/classifier/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/classifier/BUILD
index f35dc6c..37f0a30 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/classifier/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/classifier/BUILD
@@ -50,7 +50,6 @@
         ":image_classifier_src",
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/core:base_vision_api_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/detector/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/detector/BUILD
index 81249e8f..ed03c70 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/detector/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/detector/BUILD
@@ -36,7 +36,6 @@
     srcs = glob(["*.java"]) + [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/core:base_vision_api_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/searcher/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/searcher/BUILD
index fae7ce30..a36628c 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/searcher/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/searcher/BUILD
@@ -36,7 +36,6 @@
     srcs = glob(["*.java"]) + [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/core:base_vision_api_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/segmenter/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/segmenter/BUILD
index 31a2b11..9fe68d8 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/segmenter/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/segmenter/BUILD
@@ -36,7 +36,6 @@
     srcs = glob(["*.java"]) + [
         "//tensorflow_lite_support/java/src/java/org/tensorflow/lite/task/vision/core:base_vision_api_src",
     ],
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/java:tensorflowlite_support_java",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/metadata/java/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/metadata/java/BUILD
index 681a443..1138f7d 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/metadata/java/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/metadata/java/BUILD
@@ -15,7 +15,6 @@
 android_library(
     name = "tensorflowlite_support_metadata",
     srcs = METADATA_SRCS,
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//tensorflow_lite_support/metadata:metadata_schema_fbs_android",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/BUILD
index 87642d10..11b7eb1 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/tests/src/com/google/android/odml/image/BUILD
@@ -11,7 +11,6 @@
     ],
     custom_package = "com.google.android.odml.image",
     # TODO(b/163039980): Use JAVACOPTS in TF. "-Xep:RemoveUnusedImports:ERROR" wierdly break the build.
-    javacopts = ["-source 7 -target 7"],
     manifest = "AndroidManifest.xml",
     deps = [
         "@maven//:androidx_annotation_annotation",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/third_party_licenses/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/third_party_licenses/BUILD
index 80796673..4369045b1 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/third_party_licenses/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/odml/java/image/third_party_licenses/BUILD
@@ -50,7 +50,7 @@
         "AndroidX versionedparcelable library": "third_party/java/android/android_sdk_linux/extras/android/compatibility/versionedparcelable/LICENSE",
         "AndroidX viewpager library": "third_party/java/androidx/viewpager/LICENSE",
         "Animal Sniffer": "third_party/java/animal_sniffer/LICENSE",
-        "Checker Framework Annotations": "third_party/java/checker_framework_annotations/v3_21_4_eisop1/LICENSE",
+        "Checker Framework Annotations": "third_party/java/checker_framework_annotations/LICENSE",
         "Error Prone": "third_party/java/error_prone/LICENSE",
         "Google Auto": "third_party/java/auto/LICENSE",
         "Guava JDK5": "third_party/java/android_libs/guava_jdk5/LICENSE",
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/task/core/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/python/task/core/BUILD
index fcf7c78f..9954098 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/task/core/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/python/task/core/BUILD
@@ -1,7 +1,7 @@
 # Placeholder for internal Python strict library compatibility macro.
 
 package(
-    default_visibility = ["//tensorflow_lite_support:internal"],
+    default_visibility = ["//tensorflow_lite_support:users"],
     licenses = ["notice"],  # Apache 2.0
 )
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/task/processor/proto/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/python/task/processor/proto/BUILD
index 48d29a4..e733dcf1 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/task/processor/proto/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/python/task/processor/proto/BUILD
@@ -1,7 +1,7 @@
 # Placeholder for internal Python strict library compatibility macro.
 
 package(
-    default_visibility = ["//tensorflow_lite_support:internal"],
+    default_visibility = ["//tensorflow_lite_support:users"],
     licenses = ["notice"],  # Apache 2.0
 )
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/BUILD
index e3a0b5ff..965ee8b 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/BUILD
@@ -1,7 +1,7 @@
 # Placeholder for internal Python strict library compatibility macro.
 
 package(
-    default_visibility = ["//tensorflow_lite_support:internal"],
+    default_visibility = ["//tensorflow_lite_support:users"],
     licenses = ["notice"],  # Apache 2.0
 )
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/core/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/core/BUILD
index 409a5de..7bdf500 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/core/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/python/task/vision/core/BUILD
@@ -1,7 +1,7 @@
 # Placeholder for internal Python strict library compatibility macro.
 
 package(
-    default_visibility = ["//tensorflow_lite_support:internal"],
+    default_visibility = ["//tensorflow_lite_support:users"],
     licenses = ["notice"],  # Apache 2.0
 )
 
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/BUILD b/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/BUILD
index f353368..36cfd268 100644
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/BUILD
+++ b/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/BUILD
@@ -25,23 +25,4 @@
     ],
 )
 
-py_test(
-    name = "audio_classifier_test",
-    srcs = ["audio_classifier_test.py"],
-    data = [
-        "//tensorflow_lite_support/cc/test/testdata/task/audio:test_audio_clips",
-        "//tensorflow_lite_support/cc/test/testdata/task/audio:test_models",
-    ],
-    deps = [
-        # build rule placeholder: tensorflow dep,
-        "//tensorflow_lite_support/python/task/audio:audio_classifier",
-        "//tensorflow_lite_support/python/task/audio/core:audio_record",
-        "//tensorflow_lite_support/python/task/audio/core:tensor_audio",
-        "//tensorflow_lite_support/python/task/core:base_options",
-        "//tensorflow_lite_support/python/task/processor/proto:class_pb2",
-        "//tensorflow_lite_support/python/task/processor/proto:classification_options_pb2",
-        "//tensorflow_lite_support/python/task/processor/proto:classifications_pb2",
-        "//tensorflow_lite_support/python/test:test_util",
-        "@absl_py//absl/testing:parameterized",
-    ],
-)
+# TODO(b/244472798): Fix and enable these tests
diff --git a/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/audio_classifier_test.py b/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/audio_classifier_test.py
deleted file mode 100644
index 948f60e..0000000
--- a/third_party/tflite_support/src/tensorflow_lite_support/python/test/task/audio/audio_classifier_test.py
+++ /dev/null
@@ -1,329 +0,0 @@
-# Copyright 2022 The TensorFlow Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Tests for audio_classifier."""
-
-import enum
-
-from absl.testing import parameterized
-import tensorflow as tf
-
-import unittest
-from tensorflow_lite_support.python.task.audio import audio_classifier
-from tensorflow_lite_support.python.task.audio.core import audio_record
-from tensorflow_lite_support.python.task.audio.core import tensor_audio
-from tensorflow_lite_support.python.task.core import base_options as base_options_module
-from tensorflow_lite_support.python.task.processor.proto import class_pb2
-from tensorflow_lite_support.python.task.processor.proto import classification_options_pb2
-from tensorflow_lite_support.python.task.processor.proto import classifications_pb2
-from tensorflow_lite_support.python.test import test_util
-
-_mock = unittest.mock
-_BaseOptions = base_options_module.BaseOptions
-_Category = class_pb2.Category
-_Classifications = classifications_pb2.Classifications
-_ClassificationResult = classifications_pb2.ClassificationResult
-_AudioClassifier = audio_classifier.AudioClassifier
-_AudioClassifierOptions = audio_classifier.AudioClassifierOptions
-
-_FIXED_INPUT_SIZE_MODEL_FILE = 'yamnet_audio_classifier_with_metadata.tflite'
-_SPEECH_AUDIO_FILE = 'speech.wav'
-_FIXED_INPUT_SIZE_MODEL_CLASSIFICATION_RESULT = _ClassificationResult(
-    classifications=[
-        _Classifications(
-            categories=[
-                _Category(
-                    index=0,
-                    score=0.917969,
-                    display_name='',
-                    category_name='Speech'),
-                _Category(
-                    index=500,
-                    # TODO(luwa): Update tflite dep version in github repo once
-                    # cl/453375223 is submitted.
-
-                    score=0.058594,
-                    display_name='',
-                    category_name='Inside, small room'),
-                _Category(
-                    index=494,
-                    score=0.011719,
-                    display_name='',
-                    category_name='Silence')
-            ],
-            head_index=0,
-            head_name='scores')
-    ])
-
-_MULTIHEAD_MODEL_FILE = 'two_heads.tflite'
-_TWO_HEADS_AUDIO_FILE = 'two_heads.wav'
-_MULTIHEAD_MODEL_CLASSIFICATION_RESULT = _ClassificationResult(classifications=[
-    _Classifications(
-        categories=[
-            _Category(
-                index=508,
-                score=0.548616,
-                display_name='',
-                category_name='Environmental noise'),
-            _Category(
-                index=507,
-                score=0.380869,
-                display_name='',
-                category_name='Noise'),
-            _Category(
-                index=106,
-                score=0.256137,
-                display_name='',
-                category_name='Bird')
-        ],
-        head_index=0,
-        head_name='yamnet_classification'),
-    _Classifications(
-        categories=[
-            _Category(
-                index=4,
-                score=0.933997,
-                display_name='',
-                category_name='Chestnut-crowned Antpitta'),
-            _Category(
-                index=1,
-                score=0.065934,
-                display_name='',
-                category_name='White-breasted Wood-Wren'),
-            _Category(
-                index=0,
-                score=6.1469495e-05,
-                display_name='',
-                category_name='Red Crossbill')
-        ],
-        head_index=1,
-        head_name='bird_classification')
-])
-
-_ALLOW_LIST = ['Speech', 'Inside, small room']
-_DENY_LIST = ['Speech']
-_SCORE_THRESHOLD = 0.5
-_MAX_RESULTS = 3
-
-
-class ModelFileType(enum.Enum):
-  FILE_CONTENT = 1
-  FILE_NAME = 2
-
-
-def _create_classifier_from_options(base_options, **classification_options):
-  classification_options = classification_options_pb2.ClassificationOptions(
-      **classification_options)
-  options = _AudioClassifierOptions(
-      base_options=base_options, classification_options=classification_options)
-  classifier = _AudioClassifier.create_from_options(options)
-  return classifier
-
-
-class AudioClassifierTest(parameterized.TestCase, tf.test.TestCase):
-
-  def setUp(self):
-    super().setUp()
-    self.test_audio_path = test_util.get_test_data_path(_SPEECH_AUDIO_FILE)
-    self.model_path = test_util.get_test_data_path(_FIXED_INPUT_SIZE_MODEL_FILE)
-
-  def test_create_from_file_succeeds_with_valid_model_path(self):
-    # Creates with default option and valid model file successfully.
-    classifier = _AudioClassifier.create_from_file(self.model_path)
-    self.assertIsInstance(classifier, _AudioClassifier)
-
-  def test_create_from_options_succeeds_with_valid_model_path(self):
-    # Creates with options containing model file successfully.
-    base_options = _BaseOptions(file_name=self.model_path)
-    options = _AudioClassifierOptions(base_options=base_options)
-    classifier = _AudioClassifier.create_from_options(options)
-    self.assertIsInstance(classifier, _AudioClassifier)
-
-  def test_create_from_options_fails_with_invalid_model_path(self):
-    # Invalid empty model path.
-    with self.assertRaisesRegex(
-        ValueError,
-        r"ExternalFile must specify at least one of 'file_content', "
-        r"'file_name' or 'file_descriptor_meta'."):
-      base_options = _BaseOptions(file_name='')
-      options = _AudioClassifierOptions(base_options=base_options)
-      _AudioClassifier.create_from_options(options)
-
-  def test_create_from_options_succeeds_with_valid_model_content(self):
-    # Creates with options containing model content successfully.
-    with open(self.model_path, 'rb') as f:
-      base_options = _BaseOptions(file_content=f.read())
-      options = _AudioClassifierOptions(base_options=base_options)
-      classifier = _AudioClassifier.create_from_options(options)
-      self.assertIsInstance(classifier, _AudioClassifier)
-
-  def test_create_input_tensor_audio_from_classifier_succeeds(self):
-    # Creates TensorAudio instance using the classifier successfully.
-    base_options = _BaseOptions(file_name=self.model_path)
-    options = _AudioClassifierOptions(base_options=base_options)
-    classifier = _AudioClassifier.create_from_options(options)
-    self.assertIsInstance(classifier, _AudioClassifier)
-    tensor = classifier.create_input_tensor_audio()
-    self.assertIsInstance(tensor, tensor_audio.TensorAudio)
-    self.assertEqual(tensor.format.channels, 1)
-    self.assertEqual(tensor.format.sample_rate, 16000)
-    self.assertEqual(tensor.buffer_size, 15600)
-
-  @_mock.patch('sounddevice.InputStream', return_value=_mock.MagicMock())
-  def test_create_audio_record_from_classifier_succeeds(self, _):
-    # Creates AudioRecord instance using the classifier successfully.
-    base_options = _BaseOptions(file_name=self.model_path)
-    options = _AudioClassifierOptions(base_options=base_options)
-    classifier = _AudioClassifier.create_from_options(options)
-    self.assertIsInstance(classifier, _AudioClassifier)
-    record = classifier.create_audio_record()
-    self.assertIsInstance(record, audio_record.AudioRecord)
-    self.assertEqual(record.channels, 1)
-    self.assertEqual(record.sampling_rate, 16000)
-    self.assertEqual(record.buffer_size, 15600)
-
-  @parameterized.parameters(
-      (_FIXED_INPUT_SIZE_MODEL_FILE, ModelFileType.FILE_NAME,
-       _SPEECH_AUDIO_FILE, 3, _FIXED_INPUT_SIZE_MODEL_CLASSIFICATION_RESULT),
-      (_FIXED_INPUT_SIZE_MODEL_FILE, ModelFileType.FILE_CONTENT,
-       _SPEECH_AUDIO_FILE, 3, _FIXED_INPUT_SIZE_MODEL_CLASSIFICATION_RESULT),
-      (_MULTIHEAD_MODEL_FILE, ModelFileType.FILE_NAME, _TWO_HEADS_AUDIO_FILE, 3,
-       _MULTIHEAD_MODEL_CLASSIFICATION_RESULT),
-      (_MULTIHEAD_MODEL_FILE, ModelFileType.FILE_CONTENT, _TWO_HEADS_AUDIO_FILE,
-       3, _MULTIHEAD_MODEL_CLASSIFICATION_RESULT))
-  def test_classify_model(self, model_name, model_file_type, audio_file_name,
-                          max_results, expected_classification_result):
-    # Creates classifier.
-    model_path = test_util.get_test_data_path(model_name)
-    if model_file_type is ModelFileType.FILE_NAME:
-      base_options = _BaseOptions(file_name=model_path)
-    elif model_file_type is ModelFileType.FILE_CONTENT:
-      with open(model_path, 'rb') as f:
-        model_content = f.read()
-      base_options = _BaseOptions(file_content=model_content)
-    else:
-      # Should never happen
-      raise ValueError('model_file_type is invalid.')
-
-    classifier = _create_classifier_from_options(
-        base_options, max_results=max_results)
-
-    # Load the input audio file.
-    test_audio_path = test_util.get_test_data_path(audio_file_name)
-    tensor = tensor_audio.TensorAudio.create_from_wav_file(
-        test_audio_path, classifier.required_input_buffer_size)
-
-    # Classifies the input.
-    audio_result = classifier.classify(tensor)
-
-    # Comparing results.
-    self.assertProtoEquals(audio_result.to_pb2(),
-                           expected_classification_result.to_pb2())
-
-  def test_max_results_option(self):
-    # Creates classifier.
-    base_options = _BaseOptions(file_name=self.model_path)
-
-    classifier = _create_classifier_from_options(
-        base_options, max_results=_MAX_RESULTS)
-
-    # Load the input audio file.
-    tensor = tensor_audio.TensorAudio.create_from_wav_file(
-        self.test_audio_path, classifier.required_input_buffer_size)
-
-    # Classifies the input.
-    audio_result = classifier.classify(tensor)
-    categories = audio_result.classifications[0].categories
-
-    self.assertLessEqual(
-        len(categories), _MAX_RESULTS, 'Too many results returned.')
-
-  def test_score_threshold_option(self):
-    # Creates classifier.
-    base_options = _BaseOptions(file_name=self.model_path)
-
-    classifier = _create_classifier_from_options(
-        base_options, score_threshold=_SCORE_THRESHOLD)
-
-    # Load the input audio file.
-    tensor = tensor_audio.TensorAudio.create_from_wav_file(
-        self.test_audio_path, classifier.required_input_buffer_size)
-
-    # Classifies the input.
-    audio_result = classifier.classify(tensor)
-    categories = audio_result.classifications[0].categories
-
-    for category in categories:
-      self.assertGreaterEqual(
-          category.score, _SCORE_THRESHOLD,
-          'Classification with score lower than threshold found. {0}'.format(
-              category))
-
-  def test_allowlist_option(self):
-    # Creates classifier.
-    base_options = _BaseOptions(file_name=self.model_path)
-
-    classifier = _create_classifier_from_options(
-        base_options, category_name_allowlist=_ALLOW_LIST)
-
-    # Load the input audio file.
-    tensor = tensor_audio.TensorAudio.create_from_wav_file(
-        self.test_audio_path, classifier.required_input_buffer_size)
-
-    # Classifies the input.
-    audio_result = classifier.classify(tensor)
-    categories = audio_result.classifications[0].categories
-
-    for category in categories:
-      label = category.category_name
-      self.assertIn(
-          label, _ALLOW_LIST,
-          'Label "{0}" found but not in label allow list'.format(label))
-
-  def test_denylist_option(self):
-    # Creates classifier.
-    base_options = _BaseOptions(file_name=self.model_path)
-
-    classifier = _create_classifier_from_options(
-        base_options, score_threshold=0.01, category_name_denylist=_DENY_LIST)
-
-    # Load the input audio file.
-    tensor = tensor_audio.TensorAudio.create_from_wav_file(
-        self.test_audio_path, classifier.required_input_buffer_size)
-
-    # Classifies the input.
-    audio_result = classifier.classify(tensor)
-    categories = audio_result.classifications[0].categories
-
-    for category in categories:
-      label = category.category_name
-      self.assertNotIn(label, _DENY_LIST,
-                       'Label "{0}" found but in deny list.'.format(label))
-
-  def test_combined_allowlist_and_denylist(self):
-    # Fails with combined allowlist and denylist
-    with self.assertRaisesRegex(
-        ValueError,
-        r'`class_name_allowlist` and `class_name_denylist` are mutually '
-        r'exclusive options.'):
-      base_options = _BaseOptions(file_name=self.model_path)
-      classification_options = classification_options_pb2.ClassificationOptions(
-          category_name_allowlist=['foo'], category_name_denylist=['bar'])
-      options = _AudioClassifierOptions(
-          base_options=base_options,
-          classification_options=classification_options)
-      _AudioClassifier.create_from_options(options)
-
-
-if __name__ == '__main__':
-  tf.test.main()
diff --git a/third_party/tflite_support/src/third_party/tensorflow/version.bzl b/third_party/tflite_support/src/third_party/tensorflow/version.bzl
index 6d523df..807a71a 100644
--- a/third_party/tflite_support/src/third_party/tensorflow/version.bzl
+++ b/third_party/tflite_support/src/third_party/tensorflow/version.bzl
@@ -2,6 +2,6 @@
 Configures the TF version used by the TFLite Support library.
 """
 
-# TF on 2022-08-10.
-TENSORFLOW_COMMIT = "af1d5bc4fbb66d9e6cc1cf89503014a99233583b"
-TENSORFLOW_SHA256 = "f85a5443264fc58a12d136ca6a30774b5bc25ceaf7d114d97f252351b3c3a2cb"
+# TF on 2022-08-31.
+TENSORFLOW_COMMIT = "41bdbd39e756f0a2d4e6c11fe0d2f197fe58b10e"
+TENSORFLOW_SHA256 = "f64c6e15f941eac171fac1eb900d915288b6852a5aaecba122bfa1f50d739e1b"
diff --git a/third_party/wayland/BUILD.gn b/third_party/wayland/BUILD.gn
index 624a7a7..9a46435 100644
--- a/third_party/wayland/BUILD.gn
+++ b/third_party/wayland/BUILD.gn
@@ -31,7 +31,11 @@
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
 
-    public_configs = [ ":wayland_config" ]
+    if (use_system_libwayland_client) {
+      configs += [ ":wayland_config" ]
+    } else {
+      public_configs = [ ":wayland_config" ]
+    }
   }
 
   static_library("wayland_private") {
@@ -47,8 +51,13 @@
     configs += [
       "//build/config/compiler:no_chromium_code",
       "//build/config/linux/libffi",
-      ":wayland_config",
     ]
+
+    if (use_system_libwayland_client) {
+      configs += [ ":wayland_config" ]
+    } else {
+      public_configs = [ ":wayland_config" ]
+    }
   }
 
   static_library("wayland_protocol") {
@@ -59,7 +68,11 @@
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
 
-    public_configs = [ ":wayland_config" ]
+    if (use_system_libwayland_client) {
+      configs += [ ":wayland_config" ]
+    } else {
+      public_configs = [ ":wayland_config" ]
+    }
   }
 } else {
   # use_system_libwayland_client && use_system_libwayland_server
@@ -91,7 +104,11 @@
       "//build/config/linux/libffi",
     ]
 
-    public_configs = [ ":wayland_config" ]
+    if (use_system_libwayland_client) {
+      configs += [ ":wayland_config" ]
+    } else {
+      public_configs = [ ":wayland_config" ]
+    }
   }
 } else {
   pkg_config("wayland_server_config") {
@@ -153,9 +170,10 @@
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
-
-    public_configs = [ ":wayland_config" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      ":wayland_config",
+    ]
   }
 }
 
diff --git a/third_party/wayland/README.chromium b/third_party/wayland/README.chromium
index a5f05b5e..83ea8936 100644
--- a/third_party/wayland/README.chromium
+++ b/third_party/wayland/README.chromium
@@ -1,7 +1,7 @@
 Name: wayland
 URL: http://wayland.freedesktop.org/
-Version: 1.18.0
-CPEPrefix: cpe:/a:wayland:wayland:1.18.0
+Version: 1.20.0
+CPEPrefix: cpe:/a:wayland:wayland:1.20.0
 License: MIT
 License File: src/COPYING
 Security Critical: yes
@@ -16,12 +16,14 @@
 
 To import a new snapshot of wayland:
 - Checkout the latest release tag.
-  $ git checkout 1.13.0
+  $ git checkout 1.20.0
 - Update the DEPS entry to the newly checked out commit.
   $ roll-dep --roll-to=<commit>
 - Update generated files:
     (in wayland/src directory)
-    $ ./autogen.sh --disable-dtd-validation && make
-    $ rsync -R $(git ls-files --others '*.h') ../include
-    $ rsync -R $(git ls-files --others 'protocol/*-protocol.c') ..
+    $ meson build/ && ninja -C build/
+    (in wayland/src/build/src directory)
+    $ rsync $(git ls-files --others '*wayland-*protocol*.h') ../../../include/protocol/
+    $ rsync $(git ls-files --others '*-protocol.c') ../../../protocol/
+- DO NOT fix formatting in the generated files.
 - Update this README to reflect the new version number.
diff --git a/third_party/wayland/features.gni b/third_party/wayland/features.gni
index 2ca60d2..4bf0c3ac 100644
--- a/third_party/wayland/features.gni
+++ b/third_party/wayland/features.gni
@@ -5,6 +5,7 @@
 import("//build/config/chromecast_build.gni")
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/sanitizers/sanitizers.gni")
+import("//build/config/sysroot.gni")
 
 declare_args() {
   # This variable is deprecated.  use_system_libwayland_client should be used
@@ -15,14 +16,6 @@
   # Controls whether the build should use the version of Wayland
   # library shipped with the system or Chromium third_party (server library).
   use_system_libwayland_server = false
-
-  # Path to wayland-scanner. Has effect only when the system libwayland is used.
-  assert(host_os == "linux")
-  system_wayland_scanner_path = "/usr/bin/wayland-scanner"
-
-  # This may be set by Chromium packagers who do not wish to use the bundled
-  # wayland scanner.
-  use_system_wayland_scanner = false
 }
 
 declare_args() {
@@ -30,3 +23,15 @@
   # library shipped with the system or Chromium third_party (client library).
   use_system_libwayland_client = use_system_libwayland
 }
+
+declare_args() {
+  # This may be set by Chromium packagers who do not wish to use the bundled
+  # wayland scanner.
+  use_system_wayland_scanner = use_system_libwayland_client
+
+  # Path to the wayland-scanner in the sysroot.
+  assert(host_os == "linux")
+  if (use_system_libwayland_client) {
+    system_wayland_scanner_path = "$sysroot/usr/bin/wayland-scanner"
+  }
+}
diff --git a/third_party/wayland/include/config.h b/third_party/wayland/include/config.h
index 197cbe0..a212e44 100644
--- a/third_party/wayland/include/config.h
+++ b/third_party/wayland/include/config.h
@@ -1,86 +1,34 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
+/*
+ * Autogenerated by the Meson build system.
+ * Do not edit, your changes will be lost.
+ */
 
-/* Define to 1 if you have the `accept4' function. */
-#define HAVE_ACCEPT4 1
+#pragma once
 
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
+#define HAVE_ACCEPT4
 
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
+#define HAVE_BROKEN_MSG_CMSG_CLOEXEC 0
 
-/* libxml-2.0 is available */
-/* #undef HAVE_LIBXML */
+#define HAVE_MEMFD_CREATE
 
-/* Define to 1 if you have the `memfd_create' function. */
-#define HAVE_MEMFD_CREATE 1
+#define HAVE_MKOSTEMP
 
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
+#define HAVE_MREMAP
 
-/* Define to 1 if you have the `mkostemp' function. */
-#define HAVE_MKOSTEMP 1
+#define HAVE_POSIX_FALLOCATE
 
-/* Define to 1 if you have the `posix_fallocate' function. */
-#define HAVE_POSIX_FALLOCATE 1
+#define HAVE_PRCTL
 
-/* Define to 1 if you have the `prctl' function. */
-#define HAVE_PRCTL 1
+#define HAVE_STRNDUP
 
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
+#define HAVE_SYS_PRCTL_H
 
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
+#undef HAVE_SYS_PROCCTL_H
 
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
+#undef HAVE_SYS_UCRED_H
 
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
+#define HAVE_XUCRED_CR_PID 0
 
-/* Define to 1 if you have the `strndup' function. */
-#define HAVE_STRNDUP 1
-
-/* Define to 1 if you have the <sys/prctl.h> header file. */
-#define HAVE_SYS_PRCTL_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory where libtool stores uninstalled libraries. */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
 #define PACKAGE "wayland"
 
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "https://gitlab.freedesktop.org/wayland/wayland/issues/"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "wayland"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "wayland 1.18.0"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "wayland"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL "https://wayland.freedesktop.org/"
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.18.0"
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.18.0"
+#define PACKAGE_VERSION "1.20.0"
diff --git a/third_party/wayland/include/protocol/wayland-client-protocol-core.h b/third_party/wayland/include/protocol/wayland-client-protocol-core.h
index 8ae48c4..b1127a2 100644
--- a/third_party/wayland/include/protocol/wayland-client-protocol-core.h
+++ b/third_party/wayland/include/protocol/wayland-client-protocol-core.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.18.0 */
+/* Generated by wayland-scanner 1.20.0 */
 
 #ifndef WAYLAND_CLIENT_PROTOCOL_H
 #define WAYLAND_CLIENT_PROTOCOL_H
@@ -88,6 +88,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -104,6 +106,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -156,6 +161,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -172,6 +180,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -190,6 +201,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -216,6 +230,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -246,15 +263,23 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  * @section page_iface_wl_buffer_api API
  * See @ref iface_wl_buffer.
  */
@@ -262,12 +287,20 @@
  * @defgroup iface_wl_buffer The wl_buffer interface
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -292,6 +325,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -312,6 +348,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -334,6 +373,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -366,6 +408,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -394,6 +439,9 @@
  * For desktop-style user interfaces, use xdg_shell.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -428,6 +476,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -522,6 +573,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -542,6 +596,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -570,6 +627,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -586,6 +646,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -614,6 +677,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -638,6 +704,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -658,6 +727,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -708,6 +780,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -733,7 +808,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -788,7 +863,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -818,6 +893,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
@@ -959,8 +1035,8 @@
 {
 	struct wl_proxy *callback;
 
-	callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
-			 WL_DISPLAY_SYNC, &wl_callback_interface, NULL);
+	callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_display,
+			 WL_DISPLAY_SYNC, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL);
 
 	return (struct wl_callback *) callback;
 }
@@ -983,8 +1059,8 @@
 {
 	struct wl_proxy *registry;
 
-	registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
-			 WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);
+	registry = wl_proxy_marshal_flags((struct wl_proxy *) wl_display,
+			 WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL);
 
 	return (struct wl_registry *) registry;
 }
@@ -1096,8 +1172,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry,
-			 WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_registry,
+			 WL_REGISTRY_BIND, interface, version, 0, name, interface->name, version, NULL);
 
 	return (void *) id;
 }
@@ -1212,8 +1288,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
-			 WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor,
+			 WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL);
 
 	return (struct wl_surface *) id;
 }
@@ -1228,8 +1304,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
-			 WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor,
+			 WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL);
 
 	return (struct wl_region *) id;
 }
@@ -1292,8 +1368,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, NULL, offset, width, height, stride, format);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, NULL, offset, width, height, stride, format);
 
 	return (struct wl_buffer *) id;
 }
@@ -1310,10 +1386,8 @@
 static inline void
 wl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_shm_pool);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -1327,8 +1401,8 @@
 static inline void
 wl_shm_pool_resize(struct wl_shm_pool *wl_shm_pool, int32_t size)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_RESIZE, size);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, size);
 }
 
 #ifndef WL_SHM_ERROR_ENUM
@@ -1370,6 +1444,9 @@
  * The drm format codes match the macros defined in drm_fourcc.h, except
  * argb8888 and xrgb8888. The formats actually supported by the compositor
  * will be reported by the format event.
+ *
+ * For all wl_shm formats and unless specified in another protocol
+ * extension, pre-multiplied alpha is used for pixel values.
  */
 enum wl_shm_format {
 	/**
@@ -1742,6 +1819,32 @@
 	 * 2x2 subsampled Cr:Cb plane 16 bits per channel
 	 */
 	WL_SHM_FORMAT_P016 = 0x36313050,
+	/**
+	 * [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
+	 */
+	WL_SHM_FORMAT_AXBXGXRX106106106106 = 0x30314241,
+	/**
+	 * 2x2 subsampled Cr:Cb plane
+	 */
+	WL_SHM_FORMAT_NV15 = 0x3531564e,
+	WL_SHM_FORMAT_Q410 = 0x30313451,
+	WL_SHM_FORMAT_Q401 = 0x31303451,
+	/**
+	 * [63:0] x:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XRGB16161616 = 0x38345258,
+	/**
+	 * [63:0] x:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XBGR16161616 = 0x38344258,
+	/**
+	 * [63:0] A:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ARGB16161616 = 0x38345241,
+	/**
+	 * [63:0] A:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ABGR16161616 = 0x38344241,
 };
 #endif /* WL_SHM_FORMAT_ENUM */
 
@@ -1826,8 +1929,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm,
-			 WL_SHM_CREATE_POOL, &wl_shm_pool_interface, NULL, fd, size);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm,
+			 WL_SHM_CREATE_POOL, &wl_shm_pool_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm), 0, NULL, fd, size);
 
 	return (struct wl_shm_pool *) id;
 }
@@ -1911,10 +2014,8 @@
 static inline void
 wl_buffer_destroy(struct wl_buffer *wl_buffer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_buffer,
-			 WL_BUFFER_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_buffer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_buffer,
+			 WL_BUFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_buffer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_DATA_OFFER_ERROR_ENUM
@@ -2107,8 +2208,8 @@
 static inline void
 wl_data_offer_accept(struct wl_data_offer *wl_data_offer, uint32_t serial, const char *mime_type)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_ACCEPT, serial, mime_type);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_ACCEPT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, serial, mime_type);
 }
 
 /**
@@ -2133,8 +2234,8 @@
 static inline void
 wl_data_offer_receive(struct wl_data_offer *wl_data_offer, const char *mime_type, int32_t fd)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_RECEIVE, mime_type, fd);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_RECEIVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, mime_type, fd);
 }
 
 /**
@@ -2145,10 +2246,8 @@
 static inline void
 wl_data_offer_destroy(struct wl_data_offer *wl_data_offer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_offer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -2172,8 +2271,8 @@
 static inline void
 wl_data_offer_finish(struct wl_data_offer *wl_data_offer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_FINISH);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_FINISH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0);
 }
 
 /**
@@ -2214,8 +2313,8 @@
 static inline void
 wl_data_offer_set_actions(struct wl_data_offer *wl_data_offer, uint32_t dnd_actions, uint32_t preferred_action)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_SET_ACTIONS, dnd_actions, preferred_action);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, dnd_actions, preferred_action);
 }
 
 #ifndef WL_DATA_SOURCE_ERROR_ENUM
@@ -2439,8 +2538,8 @@
 static inline void
 wl_data_source_offer(struct wl_data_source *wl_data_source, const char *mime_type)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_OFFER, mime_type);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_OFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, mime_type);
 }
 
 /**
@@ -2451,10 +2550,8 @@
 static inline void
 wl_data_source_destroy(struct wl_data_source *wl_data_source)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_source);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -2477,8 +2574,8 @@
 static inline void
 wl_data_source_set_actions(struct wl_data_source *wl_data_source, uint32_t dnd_actions)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_SET_ACTIONS, dnd_actions);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, dnd_actions);
 }
 
 #ifndef WL_DATA_DEVICE_ERROR_ENUM
@@ -2586,8 +2683,10 @@
 	 * before receiving keyboard focus and when a new selection is set
 	 * while the client has keyboard focus. The data_offer is valid
 	 * until a new data_offer or NULL is received or until the client
-	 * loses keyboard focus. The client must destroy the previous
-	 * selection data_offer, if any, upon receiving this event.
+	 * loses keyboard focus. Switching surface with keyboard focus
+	 * within the same client doesn't mean a new selection will be
+	 * sent. The client must destroy the previous selection data_offer,
+	 * if any, upon receiving this event.
 	 * @param id selection data_offer object
 	 */
 	void (*selection)(void *data,
@@ -2685,7 +2784,8 @@
  * for the eventual data transfer. If source is NULL, enter, leave
  * and motion events are sent only to the client that initiated the
  * drag and the client is expected to handle the data passing
- * internally.
+ * internally. If source is destroyed, the drag-and-drop session will be
+ * cancelled.
  *
  * The origin surface is the surface where the drag originates and
  * the client must have an active implicit grab that matches the
@@ -2709,8 +2809,8 @@
 static inline void
 wl_data_device_start_drag(struct wl_data_device *wl_data_device, struct wl_data_source *source, struct wl_surface *origin, struct wl_surface *icon, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_START_DRAG, source, origin, icon, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_START_DRAG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, origin, icon, serial);
 }
 
 /**
@@ -2724,8 +2824,8 @@
 static inline void
 wl_data_device_set_selection(struct wl_data_device *wl_data_device, struct wl_data_source *source, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_SET_SELECTION, source, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_SET_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, serial);
 }
 
 /**
@@ -2736,10 +2836,8 @@
 static inline void
 wl_data_device_release(struct wl_data_device *wl_data_device)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_device);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM
@@ -2842,8 +2940,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager,
-			 WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager,
+			 WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL);
 
 	return (struct wl_data_source *) id;
 }
@@ -2858,8 +2956,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager,
-			 WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, NULL, seat);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager,
+			 WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL, seat);
 
 	return (struct wl_data_device *) id;
 }
@@ -2923,8 +3021,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shell,
-			 WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, NULL, surface);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shell,
+			 WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_shell), 0, NULL, surface);
 
 	return (struct wl_shell_surface *) id;
 }
@@ -3194,8 +3292,8 @@
 static inline void
 wl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_PONG, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, serial);
 }
 
 /**
@@ -3210,8 +3308,8 @@
 static inline void
 wl_shell_surface_move(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_MOVE, seat, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial);
 }
 
 /**
@@ -3226,8 +3324,8 @@
 static inline void
 wl_shell_surface_resize(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, uint32_t edges)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_RESIZE, seat, serial, edges);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, edges);
 }
 
 /**
@@ -3240,8 +3338,8 @@
 static inline void
 wl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TOPLEVEL);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TOPLEVEL, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0);
 }
 
 /**
@@ -3258,8 +3356,8 @@
 static inline void
 wl_shell_surface_set_transient(struct wl_shell_surface *wl_shell_surface, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TRANSIENT, parent, x, y, flags);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TRANSIENT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, parent, x, y, flags);
 }
 
 /**
@@ -3302,8 +3400,8 @@
 static inline void
 wl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, method, framerate, output);
 }
 
 /**
@@ -3332,8 +3430,8 @@
 static inline void
 wl_shell_surface_set_popup(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_POPUP, seat, serial, parent, x, y, flags);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, parent, x, y, flags);
 }
 
 /**
@@ -3361,8 +3459,8 @@
 static inline void
 wl_shell_surface_set_maximized(struct wl_shell_surface *wl_shell_surface, struct wl_output *output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_MAXIMIZED, output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, output);
 }
 
 /**
@@ -3379,8 +3477,8 @@
 static inline void
 wl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TITLE, title);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, title);
 }
 
 /**
@@ -3396,8 +3494,8 @@
 static inline void
 wl_shell_surface_set_class(struct wl_shell_surface *wl_shell_surface, const char *class_)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_CLASS, class_);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_CLASS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, class_);
 }
 
 #ifndef WL_SURFACE_ERROR_ENUM
@@ -3417,6 +3515,14 @@
 	 * buffer transform value is invalid
 	 */
 	WL_SURFACE_ERROR_INVALID_TRANSFORM = 1,
+	/**
+	 * buffer size is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_SIZE = 2,
+	/**
+	 * buffer offset is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_OFFSET = 3,
 };
 #endif /* WL_SURFACE_ERROR_ENUM */
 
@@ -3445,6 +3551,12 @@
 	 * This is emitted whenever a surface's creation, movement, or
 	 * resizing results in it no longer having any part of it within
 	 * the scanout region of an output.
+	 *
+	 * Clients should not use the number of outputs the surface is on
+	 * for frame throttling purposes. The surface might be hidden even
+	 * if no leave event has been sent, and the compositor might expect
+	 * new surface content updates even if no enter event has been
+	 * sent. The frame event should be used instead.
 	 * @param output output left by the surface
 	 */
 	void (*leave)(void *data,
@@ -3473,6 +3585,7 @@
 #define WL_SURFACE_SET_BUFFER_TRANSFORM 7
 #define WL_SURFACE_SET_BUFFER_SCALE 8
 #define WL_SURFACE_DAMAGE_BUFFER 9
+#define WL_SURFACE_OFFSET 10
 
 /**
  * @ingroup iface_wl_surface
@@ -3523,6 +3636,10 @@
  * @ingroup iface_wl_surface
  */
 #define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_surface
+ */
+#define WL_SURFACE_OFFSET_SINCE_VERSION 5
 
 /** @ingroup iface_wl_surface */
 static inline void
@@ -3552,10 +3669,8 @@
 static inline void
 wl_surface_destroy(struct wl_surface *wl_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_surface);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -3565,14 +3680,22 @@
  *
  * The new size of the surface is calculated based on the buffer
  * size transformed by the inverse buffer_transform and the
- * inverse buffer_scale. This means that the supplied buffer
- * must be an integer multiple of the buffer_scale.
+ * inverse buffer_scale. This means that at commit time the supplied
+ * buffer size must be an integer multiple of the buffer_scale. If
+ * that's not the case, an invalid_size error is sent.
  *
  * The x and y arguments specify the location of the new pending
  * buffer's upper left corner, relative to the current buffer's upper
  * left corner, in surface-local coordinates. In other words, the
  * x and y, combined with the new surface size define in which
- * directions the surface's size changes.
+ * directions the surface's size changes. Setting anything other than 0
+ * as x and y arguments is discouraged, and should instead be replaced
+ * with using the separate wl_surface.offset request.
+ *
+ * When the bound wl_surface version is 5 or higher, passing any
+ * non-zero x or y is a protocol violation, and will result in an
+ * 'invalid_offset' error being raised. To achieve equivalent semantics,
+ * use wl_surface.offset.
  *
  * Surface contents are double-buffered state, see wl_surface.commit.
  *
@@ -3600,9 +3723,12 @@
  * from the same backing storage or use wp_linux_buffer_release.
  *
  * Destroying the wl_buffer after wl_buffer.release does not change
- * the surface contents. However, if the client destroys the
- * wl_buffer before receiving the wl_buffer.release event, the surface
- * contents become undefined immediately.
+ * the surface contents. Destroying the wl_buffer before wl_buffer.release
+ * is allowed as long as the underlying buffer storage isn't re-used (this
+ * can happen e.g. on client process termination). However, if the client
+ * destroys the wl_buffer before receiving the wl_buffer.release event and
+ * mutates the underlying buffer storage, the surface contents become
+ * undefined immediately.
  *
  * If wl_surface.attach is sent with a NULL wl_buffer, the
  * following wl_surface.commit will remove the surface content.
@@ -3610,8 +3736,8 @@
 static inline void
 wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_ATTACH, buffer, x, y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_ATTACH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, buffer, x, y);
 }
 
 /**
@@ -3642,8 +3768,8 @@
 static inline void
 wl_surface_damage(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DAMAGE, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DAMAGE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height);
 }
 
 /**
@@ -3687,8 +3813,8 @@
 {
 	struct wl_proxy *callback;
 
-	callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_FRAME, &wl_callback_interface, NULL);
+	callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_FRAME, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, NULL);
 
 	return (struct wl_callback *) callback;
 }
@@ -3724,8 +3850,8 @@
 static inline void
 wl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_OPAQUE_REGION, region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_OPAQUE_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region);
 }
 
 /**
@@ -3757,8 +3883,8 @@
 static inline void
 wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_INPUT_REGION, region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_INPUT_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region);
 }
 
 /**
@@ -3785,8 +3911,8 @@
 static inline void
 wl_surface_commit(struct wl_surface *wl_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_COMMIT);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_COMMIT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0);
 }
 
 /**
@@ -3825,8 +3951,8 @@
 static inline void
 wl_surface_set_buffer_transform(struct wl_surface *wl_surface, int32_t transform)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_BUFFER_TRANSFORM, transform);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_BUFFER_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, transform);
 }
 
 /**
@@ -3859,8 +3985,8 @@
 static inline void
 wl_surface_set_buffer_scale(struct wl_surface *wl_surface, int32_t scale)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_BUFFER_SCALE, scale);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_BUFFER_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, scale);
 }
 
 /**
@@ -3902,8 +4028,31 @@
 static inline void
 wl_surface_damage_buffer(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DAMAGE_BUFFER, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DAMAGE_BUFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height);
+}
+
+/**
+ * @ingroup iface_wl_surface
+ *
+ * The x and y arguments specify the location of the new pending
+ * buffer's upper left corner, relative to the current buffer's upper
+ * left corner, in surface-local coordinates. In other words, the
+ * x and y, combined with the new surface size define in which
+ * directions the surface's size changes.
+ *
+ * Surface location offset is double-buffered state, see
+ * wl_surface.commit.
+ *
+ * This request is semantically equivalent to and the replaces the x and y
+ * arguments in the wl_surface.attach request in wl_surface versions prior
+ * to 5. See wl_surface.attach for details.
+ */
+static inline void
+wl_surface_offset(struct wl_surface *wl_surface, int32_t x, int32_t y)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y);
 }
 
 #ifndef WL_SEAT_CAPABILITY_ENUM
@@ -3931,6 +4080,22 @@
 };
 #endif /* WL_SEAT_CAPABILITY_ENUM */
 
+#ifndef WL_SEAT_ERROR_ENUM
+#define WL_SEAT_ERROR_ENUM
+/**
+ * @ingroup iface_wl_seat
+ * wl_seat error values
+ *
+ * These errors can be emitted in response to wl_seat requests.
+ */
+enum wl_seat_error {
+	/**
+	 * get_pointer, get_keyboard or get_touch called on seat without the matching capability
+	 */
+	WL_SEAT_ERROR_MISSING_CAPABILITY = 0,
+};
+#endif /* WL_SEAT_ERROR_ENUM */
+
 /**
  * @ingroup iface_wl_seat
  * @struct wl_seat_listener
@@ -3972,9 +4137,25 @@
 	/**
 	 * unique identifier for this seat
 	 *
-	 * In a multiseat configuration this can be used by the client to
-	 * help identify which physical devices the seat represents. Based
-	 * on the seat configuration used by the compositor.
+	 * In a multi-seat configuration the seat name can be used by
+	 * clients to help identify which physical devices the seat
+	 * represents.
+	 *
+	 * The seat name is a UTF-8 string with no convention defined for
+	 * its contents. Each name is unique among all wl_seat globals. The
+	 * name is only guaranteed to be unique for the current compositor
+	 * instance.
+	 *
+	 * The same seat names are used for all clients. Thus, the name can
+	 * be shared across processes to refer to a specific wl_seat
+	 * global.
+	 *
+	 * The name event is sent after binding to the seat global. This
+	 * event is only sent once per seat object, and the name does not
+	 * change over the lifetime of the wl_seat global.
+	 *
+	 * Compositors may re-use the same seat name if the wl_seat global
+	 * is destroyed and re-created later.
 	 * @param name seat identifier
 	 * @since 2
 	 */
@@ -4061,15 +4242,16 @@
  * This request only takes effect if the seat has the pointer
  * capability, or has had the pointer capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the pointer capability.
+ * never had the pointer capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_pointer *
 wl_seat_get_pointer(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_POINTER, &wl_pointer_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_POINTER, &wl_pointer_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_pointer *) id;
 }
@@ -4083,15 +4265,16 @@
  * This request only takes effect if the seat has the keyboard
  * capability, or has had the keyboard capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the keyboard capability.
+ * never had the keyboard capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_keyboard *
 wl_seat_get_keyboard(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_keyboard *) id;
 }
@@ -4105,15 +4288,16 @@
  * This request only takes effect if the seat has the touch
  * capability, or has had the touch capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the touch capability.
+ * never had the touch capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_touch *
 wl_seat_get_touch(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_TOUCH, &wl_touch_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_TOUCH, &wl_touch_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_touch *) id;
 }
@@ -4127,10 +4311,8 @@
 static inline void
 wl_seat_release(struct wl_seat *wl_seat)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_seat,
-			 WL_SEAT_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_seat);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_seat), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_POINTER_ERROR_ENUM
@@ -4603,12 +4785,16 @@
  * wl_surface is no longer used as the cursor. When the use as a
  * cursor ends, the current and pending input regions become
  * undefined, and the wl_surface is unmapped.
+ *
+ * The serial parameter must match the latest wl_pointer.enter
+ * serial number sent to the client. Otherwise the request will be
+ * ignored.
  */
 static inline void
 wl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, int32_t hotspot_x, int32_t hotspot_y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_pointer,
-			 WL_POINTER_SET_CURSOR, serial, surface, hotspot_x, hotspot_y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer,
+			 WL_POINTER_SET_CURSOR, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), 0, serial, surface, hotspot_x, hotspot_y);
 }
 
 /**
@@ -4623,10 +4809,8 @@
 static inline void
 wl_pointer_release(struct wl_pointer *wl_pointer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_pointer,
-			 WL_POINTER_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_pointer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer,
+			 WL_POINTER_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM
@@ -4679,7 +4863,8 @@
 	 * keyboard mapping
 	 *
 	 * This event provides a file descriptor to the client which can
-	 * be memory-mapped to provide a keyboard mapping description.
+	 * be memory-mapped in read-only mode to provide a keyboard mapping
+	 * description.
 	 *
 	 * From version 7 onwards, the fd must be mapped with MAP_PRIVATE
 	 * by the recipient, as MAP_SHARED may fail.
@@ -4697,6 +4882,9 @@
 	 *
 	 * Notification that this seat's keyboard focus is on a certain
 	 * surface.
+	 *
+	 * The compositor must send the wl_keyboard.modifiers event after
+	 * this event.
 	 * @param serial serial number of the enter event
 	 * @param surface surface gaining keyboard focus
 	 * @param keys the currently pressed keys
@@ -4714,6 +4902,10 @@
 	 *
 	 * The leave notification is sent before the enter notification for
 	 * the new focus.
+	 *
+	 * After this event client must assume that all keys, including
+	 * modifiers, are lifted and also it must stop key repeating if
+	 * there's some going on.
 	 * @param serial serial number of the leave event
 	 * @param surface surface that lost keyboard focus
 	 */
@@ -4726,6 +4918,12 @@
 	 *
 	 * A key was pressed or released. The time argument is a
 	 * timestamp with millisecond granularity, with an undefined base.
+	 *
+	 * The key is a platform-specific key code that can be interpreted
+	 * by feeding it to the keyboard mapping (see the keymap event).
+	 *
+	 * If this event produces a change in modifiers, then the resulting
+	 * wl_keyboard.modifiers event must be sent after this event.
 	 * @param serial serial number of the key event
 	 * @param time timestamp with millisecond granularity
 	 * @param key key that produced the event
@@ -4857,10 +5055,8 @@
 static inline void
 wl_keyboard_release(struct wl_keyboard *wl_keyboard)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_keyboard,
-			 WL_KEYBOARD_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_keyboard);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_keyboard,
+			 WL_KEYBOARD_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_keyboard), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5101,10 +5297,8 @@
 static inline void
 wl_touch_release(struct wl_touch *wl_touch)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_touch,
-			 WL_TOUCH_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_touch);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_touch,
+			 WL_TOUCH_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_touch), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_OUTPUT_SUBPIXEL_ENUM
@@ -5234,13 +5428,16 @@
 	 * The physical size can be set to zero if it doesn't make sense
 	 * for this output (e.g. for projectors or virtual outputs).
 	 *
+	 * The geometry event will be followed by a done event (starting
+	 * from version 2).
+	 *
 	 * Note: wl_output only advertises partial information about the
 	 * output position and identification. Some compositors, for
 	 * instance those not implementing a desktop-style output layout or
 	 * those exposing virtual outputs, might fake this information.
 	 * Instead of using x and y, clients should use
 	 * xdg_output.logical_position. Instead of using make and model,
-	 * clients should use xdg_output.name and xdg_output.description.
+	 * clients should use name and description.
 	 * @param x x position within the global compositor space
 	 * @param y y position within the global compositor space
 	 * @param physical_width width in millimeters of the output
@@ -5271,6 +5468,10 @@
 	 * current. In other words, the current mode is always the last
 	 * mode that was received with the current flag set.
 	 *
+	 * Non-current modes are deprecated. A compositor can decide to
+	 * only advertise the current mode and never send other modes.
+	 * Clients should not rely on non-current modes.
+	 *
 	 * The size of a mode is given in physical hardware units of the
 	 * output device. This is not necessarily the same as the output
 	 * size in the global compositor space. For instance, the output
@@ -5282,6 +5483,9 @@
 	 * The vertical refresh rate can be set to zero if it doesn't make
 	 * sense for this output (e.g. for virtual outputs).
 	 *
+	 * The mode event will be followed by a done event (starting from
+	 * version 2).
+	 *
 	 * Clients should not use the refresh rate to schedule frames.
 	 * Instead, they should use the wl_surface.frame event or the
 	 * presentation-time protocol.
@@ -5331,12 +5535,77 @@
 	 * use wl_surface.set_buffer_scale with the scale of the output.
 	 * That way the compositor can avoid scaling the surface, and the
 	 * client can supply a higher detail image.
+	 *
+	 * The scale event will be followed by a done event.
 	 * @param factor scaling factor of output
 	 * @since 2
 	 */
 	void (*scale)(void *data,
 		      struct wl_output *wl_output,
 		      int32_t factor);
+	/**
+	 * name of this output
+	 *
+	 * Many compositors will assign user-friendly names to their
+	 * outputs, show them to the user, allow the user to refer to an
+	 * output, etc. The client may wish to know this name as well to
+	 * offer the user similar behaviors.
+	 *
+	 * The name is a UTF-8 string with no convention defined for its
+	 * contents. Each name is unique among all wl_output globals. The
+	 * name is only guaranteed to be unique for the compositor
+	 * instance.
+	 *
+	 * The same output name is used for all clients for a given
+	 * wl_output global. Thus, the name can be shared across processes
+	 * to refer to a specific wl_output global.
+	 *
+	 * The name is not guaranteed to be persistent across sessions,
+	 * thus cannot be used to reliably identify an output in e.g.
+	 * configuration files.
+	 *
+	 * Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc.
+	 * However, do not assume that the name is a reflection of an
+	 * underlying DRM connector, X11 connection, etc.
+	 *
+	 * The name event is sent after binding the output object. This
+	 * event is only sent once per output object, and the name does not
+	 * change over the lifetime of the wl_output global.
+	 *
+	 * Compositors may re-use the same output name if the wl_output
+	 * global is destroyed and re-created later. Compositors should
+	 * avoid re-using the same name if possible.
+	 *
+	 * The name event will be followed by a done event.
+	 * @param name output name
+	 * @since 4
+	 */
+	void (*name)(void *data,
+		     struct wl_output *wl_output,
+		     const char *name);
+	/**
+	 * human-readable description of this output
+	 *
+	 * Many compositors can produce human-readable descriptions of
+	 * their outputs. The client may wish to know this description as
+	 * well, e.g. for output selection purposes.
+	 *
+	 * The description is a UTF-8 string with no convention defined for
+	 * its contents. The description is not guaranteed to be unique
+	 * among all wl_output globals. Examples might include 'Foocorp 11"
+	 * Display' or 'Virtual X11 output via :1'.
+	 *
+	 * The description event is sent after binding the output object
+	 * and whenever the description changes. The description is
+	 * optional, and may not be sent at all.
+	 *
+	 * The description event will be followed by a done event.
+	 * @param description output description
+	 * @since 4
+	 */
+	void (*description)(void *data,
+			    struct wl_output *wl_output,
+			    const char *description);
 };
 
 /**
@@ -5368,6 +5637,14 @@
  * @ingroup iface_wl_output
  */
 #define WL_OUTPUT_SCALE_SINCE_VERSION 2
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_NAME_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_DESCRIPTION_SINCE_VERSION 4
 
 /**
  * @ingroup iface_wl_output
@@ -5410,10 +5687,8 @@
 static inline void
 wl_output_release(struct wl_output *wl_output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_output,
-			 WL_OUTPUT_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_output,
+			 WL_OUTPUT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_output), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #define WL_REGION_DESTROY 0
@@ -5462,10 +5737,8 @@
 static inline void
 wl_region_destroy(struct wl_region *wl_region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5476,8 +5749,8 @@
 static inline void
 wl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_ADD, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_ADD, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height);
 }
 
 /**
@@ -5488,8 +5761,8 @@
 static inline void
 wl_region_subtract(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_SUBTRACT, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_SUBTRACT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height);
 }
 
 #ifndef WL_SUBCOMPOSITOR_ERROR_ENUM
@@ -5545,10 +5818,8 @@
 static inline void
 wl_subcompositor_destroy(struct wl_subcompositor *wl_subcompositor)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subcompositor,
-			 WL_SUBCOMPOSITOR_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_subcompositor);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor,
+			 WL_SUBCOMPOSITOR_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5575,8 +5846,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_subcompositor,
-			 WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, NULL, surface, parent);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor,
+			 WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), 0, NULL, surface, parent);
 
 	return (struct wl_subsurface *) id;
 }
@@ -5656,10 +5927,8 @@
 static inline void
 wl_subsurface_destroy(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_subsurface);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5685,8 +5954,8 @@
 static inline void
 wl_subsurface_set_position(struct wl_subsurface *wl_subsurface, int32_t x, int32_t y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_POSITION, x, y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, x, y);
 }
 
 /**
@@ -5711,8 +5980,8 @@
 static inline void
 wl_subsurface_place_above(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_PLACE_ABOVE, sibling);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_PLACE_ABOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling);
 }
 
 /**
@@ -5724,8 +5993,8 @@
 static inline void
 wl_subsurface_place_below(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_PLACE_BELOW, sibling);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_PLACE_BELOW, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling);
 }
 
 /**
@@ -5748,8 +6017,8 @@
 static inline void
 wl_subsurface_set_sync(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_SYNC);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0);
 }
 
 /**
@@ -5778,8 +6047,8 @@
 static inline void
 wl_subsurface_set_desync(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_DESYNC);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_DESYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0);
 }
 
 #ifdef  __cplusplus
diff --git a/third_party/wayland/include/protocol/wayland-client-protocol.h b/third_party/wayland/include/protocol/wayland-client-protocol.h
index fae49a1..550147f 100644
--- a/third_party/wayland/include/protocol/wayland-client-protocol.h
+++ b/third_party/wayland/include/protocol/wayland-client-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.18.0 */
+/* Generated by wayland-scanner 1.20.0 */
 
 #ifndef WAYLAND_CLIENT_PROTOCOL_H
 #define WAYLAND_CLIENT_PROTOCOL_H
@@ -88,6 +88,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -104,6 +106,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -156,6 +161,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -172,6 +180,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -190,6 +201,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -216,6 +230,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -246,15 +263,23 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  * @section page_iface_wl_buffer_api API
  * See @ref iface_wl_buffer.
  */
@@ -262,12 +287,20 @@
  * @defgroup iface_wl_buffer The wl_buffer interface
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -292,6 +325,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -312,6 +348,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -334,6 +373,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -366,6 +408,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -394,6 +439,9 @@
  * For desktop-style user interfaces, use xdg_shell.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -428,6 +476,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -522,6 +573,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -542,6 +596,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -570,6 +627,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -586,6 +646,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -614,6 +677,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -638,6 +704,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -658,6 +727,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -708,6 +780,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -733,7 +808,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -788,7 +863,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -818,6 +893,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
@@ -959,8 +1035,8 @@
 {
 	struct wl_proxy *callback;
 
-	callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
-			 WL_DISPLAY_SYNC, &wl_callback_interface, NULL);
+	callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_display,
+			 WL_DISPLAY_SYNC, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL);
 
 	return (struct wl_callback *) callback;
 }
@@ -983,8 +1059,8 @@
 {
 	struct wl_proxy *registry;
 
-	registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
-			 WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);
+	registry = wl_proxy_marshal_flags((struct wl_proxy *) wl_display,
+			 WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, wl_proxy_get_version((struct wl_proxy *) wl_display), 0, NULL);
 
 	return (struct wl_registry *) registry;
 }
@@ -1096,8 +1172,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry,
-			 WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_registry,
+			 WL_REGISTRY_BIND, interface, version, 0, name, interface->name, version, NULL);
 
 	return (void *) id;
 }
@@ -1212,8 +1288,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
-			 WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor,
+			 WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL);
 
 	return (struct wl_surface *) id;
 }
@@ -1228,8 +1304,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
-			 WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_compositor,
+			 WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, wl_proxy_get_version((struct wl_proxy *) wl_compositor), 0, NULL);
 
 	return (struct wl_region *) id;
 }
@@ -1292,8 +1368,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, NULL, offset, width, height, stride, format);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, NULL, offset, width, height, stride, format);
 
 	return (struct wl_buffer *) id;
 }
@@ -1310,10 +1386,8 @@
 static inline void
 wl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_shm_pool);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -1327,8 +1401,8 @@
 static inline void
 wl_shm_pool_resize(struct wl_shm_pool *wl_shm_pool, int32_t size)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
-			 WL_SHM_POOL_RESIZE, size);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shm_pool,
+			 WL_SHM_POOL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shm_pool), 0, size);
 }
 
 #ifndef WL_SHM_ERROR_ENUM
@@ -1370,6 +1444,9 @@
  * The drm format codes match the macros defined in drm_fourcc.h, except
  * argb8888 and xrgb8888. The formats actually supported by the compositor
  * will be reported by the format event.
+ *
+ * For all wl_shm formats and unless specified in another protocol
+ * extension, pre-multiplied alpha is used for pixel values.
  */
 enum wl_shm_format {
 	/**
@@ -1742,6 +1819,32 @@
 	 * 2x2 subsampled Cr:Cb plane 16 bits per channel
 	 */
 	WL_SHM_FORMAT_P016 = 0x36313050,
+	/**
+	 * [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
+	 */
+	WL_SHM_FORMAT_AXBXGXRX106106106106 = 0x30314241,
+	/**
+	 * 2x2 subsampled Cr:Cb plane
+	 */
+	WL_SHM_FORMAT_NV15 = 0x3531564e,
+	WL_SHM_FORMAT_Q410 = 0x30313451,
+	WL_SHM_FORMAT_Q401 = 0x31303451,
+	/**
+	 * [63:0] x:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XRGB16161616 = 0x38345258,
+	/**
+	 * [63:0] x:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XBGR16161616 = 0x38344258,
+	/**
+	 * [63:0] A:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ARGB16161616 = 0x38345241,
+	/**
+	 * [63:0] A:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ABGR16161616 = 0x38344241,
 };
 #endif /* WL_SHM_FORMAT_ENUM */
 
@@ -1826,8 +1929,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm,
-			 WL_SHM_CREATE_POOL, &wl_shm_pool_interface, NULL, fd, size);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shm,
+			 WL_SHM_CREATE_POOL, &wl_shm_pool_interface, wl_proxy_get_version((struct wl_proxy *) wl_shm), 0, NULL, fd, size);
 
 	return (struct wl_shm_pool *) id;
 }
@@ -1911,10 +2014,8 @@
 static inline void
 wl_buffer_destroy(struct wl_buffer *wl_buffer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_buffer,
-			 WL_BUFFER_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_buffer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_buffer,
+			 WL_BUFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_buffer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_DATA_OFFER_ERROR_ENUM
@@ -2107,8 +2208,8 @@
 static inline void
 wl_data_offer_accept(struct wl_data_offer *wl_data_offer, uint32_t serial, const char *mime_type)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_ACCEPT, serial, mime_type);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_ACCEPT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, serial, mime_type);
 }
 
 /**
@@ -2133,8 +2234,8 @@
 static inline void
 wl_data_offer_receive(struct wl_data_offer *wl_data_offer, const char *mime_type, int32_t fd)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_RECEIVE, mime_type, fd);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_RECEIVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, mime_type, fd);
 }
 
 /**
@@ -2145,10 +2246,8 @@
 static inline void
 wl_data_offer_destroy(struct wl_data_offer *wl_data_offer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_offer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -2172,8 +2271,8 @@
 static inline void
 wl_data_offer_finish(struct wl_data_offer *wl_data_offer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_FINISH);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_FINISH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0);
 }
 
 /**
@@ -2214,8 +2313,8 @@
 static inline void
 wl_data_offer_set_actions(struct wl_data_offer *wl_data_offer, uint32_t dnd_actions, uint32_t preferred_action)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_offer,
-			 WL_DATA_OFFER_SET_ACTIONS, dnd_actions, preferred_action);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_offer,
+			 WL_DATA_OFFER_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_offer), 0, dnd_actions, preferred_action);
 }
 
 #ifndef WL_DATA_SOURCE_ERROR_ENUM
@@ -2439,8 +2538,8 @@
 static inline void
 wl_data_source_offer(struct wl_data_source *wl_data_source, const char *mime_type)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_OFFER, mime_type);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_OFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, mime_type);
 }
 
 /**
@@ -2451,10 +2550,8 @@
 static inline void
 wl_data_source_destroy(struct wl_data_source *wl_data_source)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_source);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -2477,8 +2574,8 @@
 static inline void
 wl_data_source_set_actions(struct wl_data_source *wl_data_source, uint32_t dnd_actions)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_source,
-			 WL_DATA_SOURCE_SET_ACTIONS, dnd_actions);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_source,
+			 WL_DATA_SOURCE_SET_ACTIONS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_source), 0, dnd_actions);
 }
 
 #ifndef WL_DATA_DEVICE_ERROR_ENUM
@@ -2586,8 +2683,10 @@
 	 * before receiving keyboard focus and when a new selection is set
 	 * while the client has keyboard focus. The data_offer is valid
 	 * until a new data_offer or NULL is received or until the client
-	 * loses keyboard focus. The client must destroy the previous
-	 * selection data_offer, if any, upon receiving this event.
+	 * loses keyboard focus. Switching surface with keyboard focus
+	 * within the same client doesn't mean a new selection will be
+	 * sent. The client must destroy the previous selection data_offer,
+	 * if any, upon receiving this event.
 	 * @param id selection data_offer object
 	 */
 	void (*selection)(void *data,
@@ -2685,7 +2784,8 @@
  * for the eventual data transfer. If source is NULL, enter, leave
  * and motion events are sent only to the client that initiated the
  * drag and the client is expected to handle the data passing
- * internally.
+ * internally. If source is destroyed, the drag-and-drop session will be
+ * cancelled.
  *
  * The origin surface is the surface where the drag originates and
  * the client must have an active implicit grab that matches the
@@ -2709,8 +2809,8 @@
 static inline void
 wl_data_device_start_drag(struct wl_data_device *wl_data_device, struct wl_data_source *source, struct wl_surface *origin, struct wl_surface *icon, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_START_DRAG, source, origin, icon, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_START_DRAG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, origin, icon, serial);
 }
 
 /**
@@ -2724,8 +2824,8 @@
 static inline void
 wl_data_device_set_selection(struct wl_data_device *wl_data_device, struct wl_data_source *source, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_SET_SELECTION, source, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_SET_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), 0, source, serial);
 }
 
 /**
@@ -2736,10 +2836,8 @@
 static inline void
 wl_data_device_release(struct wl_data_device *wl_data_device)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_data_device,
-			 WL_DATA_DEVICE_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_data_device);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device,
+			 WL_DATA_DEVICE_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_data_device), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM
@@ -2842,8 +2940,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager,
-			 WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager,
+			 WL_DATA_DEVICE_MANAGER_CREATE_DATA_SOURCE, &wl_data_source_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL);
 
 	return (struct wl_data_source *) id;
 }
@@ -2858,8 +2956,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_data_device_manager,
-			 WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, NULL, seat);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_data_device_manager,
+			 WL_DATA_DEVICE_MANAGER_GET_DATA_DEVICE, &wl_data_device_interface, wl_proxy_get_version((struct wl_proxy *) wl_data_device_manager), 0, NULL, seat);
 
 	return (struct wl_data_device *) id;
 }
@@ -2923,8 +3021,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shell,
-			 WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, NULL, surface);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_shell,
+			 WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_shell), 0, NULL, surface);
 
 	return (struct wl_shell_surface *) id;
 }
@@ -3194,8 +3292,8 @@
 static inline void
 wl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_PONG, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, serial);
 }
 
 /**
@@ -3210,8 +3308,8 @@
 static inline void
 wl_shell_surface_move(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_MOVE, seat, serial);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial);
 }
 
 /**
@@ -3226,8 +3324,8 @@
 static inline void
 wl_shell_surface_resize(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, uint32_t edges)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_RESIZE, seat, serial, edges);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, edges);
 }
 
 /**
@@ -3240,8 +3338,8 @@
 static inline void
 wl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TOPLEVEL);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TOPLEVEL, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0);
 }
 
 /**
@@ -3258,8 +3356,8 @@
 static inline void
 wl_shell_surface_set_transient(struct wl_shell_surface *wl_shell_surface, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TRANSIENT, parent, x, y, flags);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TRANSIENT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, parent, x, y, flags);
 }
 
 /**
@@ -3302,8 +3400,8 @@
 static inline void
 wl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, method, framerate, output);
 }
 
 /**
@@ -3332,8 +3430,8 @@
 static inline void
 wl_shell_surface_set_popup(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_POPUP, seat, serial, parent, x, y, flags);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_POPUP, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, seat, serial, parent, x, y, flags);
 }
 
 /**
@@ -3361,8 +3459,8 @@
 static inline void
 wl_shell_surface_set_maximized(struct wl_shell_surface *wl_shell_surface, struct wl_output *output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_MAXIMIZED, output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, output);
 }
 
 /**
@@ -3379,8 +3477,8 @@
 static inline void
 wl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_TITLE, title);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, title);
 }
 
 /**
@@ -3396,8 +3494,8 @@
 static inline void
 wl_shell_surface_set_class(struct wl_shell_surface *wl_shell_surface, const char *class_)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
-			 WL_SHELL_SURFACE_SET_CLASS, class_);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_shell_surface,
+			 WL_SHELL_SURFACE_SET_CLASS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_shell_surface), 0, class_);
 }
 
 #ifndef WL_SURFACE_ERROR_ENUM
@@ -3417,6 +3515,14 @@
 	 * buffer transform value is invalid
 	 */
 	WL_SURFACE_ERROR_INVALID_TRANSFORM = 1,
+	/**
+	 * buffer size is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_SIZE = 2,
+	/**
+	 * buffer offset is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_OFFSET = 3,
 };
 #endif /* WL_SURFACE_ERROR_ENUM */
 
@@ -3445,6 +3551,12 @@
 	 * This is emitted whenever a surface's creation, movement, or
 	 * resizing results in it no longer having any part of it within
 	 * the scanout region of an output.
+	 *
+	 * Clients should not use the number of outputs the surface is on
+	 * for frame throttling purposes. The surface might be hidden even
+	 * if no leave event has been sent, and the compositor might expect
+	 * new surface content updates even if no enter event has been
+	 * sent. The frame event should be used instead.
 	 * @param output output left by the surface
 	 */
 	void (*leave)(void *data,
@@ -3473,6 +3585,7 @@
 #define WL_SURFACE_SET_BUFFER_TRANSFORM 7
 #define WL_SURFACE_SET_BUFFER_SCALE 8
 #define WL_SURFACE_DAMAGE_BUFFER 9
+#define WL_SURFACE_OFFSET 10
 
 /**
  * @ingroup iface_wl_surface
@@ -3523,6 +3636,10 @@
  * @ingroup iface_wl_surface
  */
 #define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_surface
+ */
+#define WL_SURFACE_OFFSET_SINCE_VERSION 5
 
 /** @ingroup iface_wl_surface */
 static inline void
@@ -3552,10 +3669,8 @@
 static inline void
 wl_surface_destroy(struct wl_surface *wl_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_surface);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -3565,14 +3680,22 @@
  *
  * The new size of the surface is calculated based on the buffer
  * size transformed by the inverse buffer_transform and the
- * inverse buffer_scale. This means that the supplied buffer
- * must be an integer multiple of the buffer_scale.
+ * inverse buffer_scale. This means that at commit time the supplied
+ * buffer size must be an integer multiple of the buffer_scale. If
+ * that's not the case, an invalid_size error is sent.
  *
  * The x and y arguments specify the location of the new pending
  * buffer's upper left corner, relative to the current buffer's upper
  * left corner, in surface-local coordinates. In other words, the
  * x and y, combined with the new surface size define in which
- * directions the surface's size changes.
+ * directions the surface's size changes. Setting anything other than 0
+ * as x and y arguments is discouraged, and should instead be replaced
+ * with using the separate wl_surface.offset request.
+ *
+ * When the bound wl_surface version is 5 or higher, passing any
+ * non-zero x or y is a protocol violation, and will result in an
+ * 'invalid_offset' error being raised. To achieve equivalent semantics,
+ * use wl_surface.offset.
  *
  * Surface contents are double-buffered state, see wl_surface.commit.
  *
@@ -3600,9 +3723,12 @@
  * from the same backing storage or use wp_linux_buffer_release.
  *
  * Destroying the wl_buffer after wl_buffer.release does not change
- * the surface contents. However, if the client destroys the
- * wl_buffer before receiving the wl_buffer.release event, the surface
- * contents become undefined immediately.
+ * the surface contents. Destroying the wl_buffer before wl_buffer.release
+ * is allowed as long as the underlying buffer storage isn't re-used (this
+ * can happen e.g. on client process termination). However, if the client
+ * destroys the wl_buffer before receiving the wl_buffer.release event and
+ * mutates the underlying buffer storage, the surface contents become
+ * undefined immediately.
  *
  * If wl_surface.attach is sent with a NULL wl_buffer, the
  * following wl_surface.commit will remove the surface content.
@@ -3610,8 +3736,8 @@
 static inline void
 wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_ATTACH, buffer, x, y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_ATTACH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, buffer, x, y);
 }
 
 /**
@@ -3642,8 +3768,8 @@
 static inline void
 wl_surface_damage(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DAMAGE, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DAMAGE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height);
 }
 
 /**
@@ -3687,8 +3813,8 @@
 {
 	struct wl_proxy *callback;
 
-	callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_FRAME, &wl_callback_interface, NULL);
+	callback = wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_FRAME, &wl_callback_interface, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, NULL);
 
 	return (struct wl_callback *) callback;
 }
@@ -3724,8 +3850,8 @@
 static inline void
 wl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_OPAQUE_REGION, region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_OPAQUE_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region);
 }
 
 /**
@@ -3757,8 +3883,8 @@
 static inline void
 wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_INPUT_REGION, region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_INPUT_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, region);
 }
 
 /**
@@ -3785,8 +3911,8 @@
 static inline void
 wl_surface_commit(struct wl_surface *wl_surface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_COMMIT);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_COMMIT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0);
 }
 
 /**
@@ -3825,8 +3951,8 @@
 static inline void
 wl_surface_set_buffer_transform(struct wl_surface *wl_surface, int32_t transform)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_BUFFER_TRANSFORM, transform);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_BUFFER_TRANSFORM, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, transform);
 }
 
 /**
@@ -3859,8 +3985,8 @@
 static inline void
 wl_surface_set_buffer_scale(struct wl_surface *wl_surface, int32_t scale)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_SET_BUFFER_SCALE, scale);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_SET_BUFFER_SCALE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, scale);
 }
 
 /**
@@ -3902,8 +4028,31 @@
 static inline void
 wl_surface_damage_buffer(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_surface,
-			 WL_SURFACE_DAMAGE_BUFFER, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_DAMAGE_BUFFER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y, width, height);
+}
+
+/**
+ * @ingroup iface_wl_surface
+ *
+ * The x and y arguments specify the location of the new pending
+ * buffer's upper left corner, relative to the current buffer's upper
+ * left corner, in surface-local coordinates. In other words, the
+ * x and y, combined with the new surface size define in which
+ * directions the surface's size changes.
+ *
+ * Surface location offset is double-buffered state, see
+ * wl_surface.commit.
+ *
+ * This request is semantically equivalent to and the replaces the x and y
+ * arguments in the wl_surface.attach request in wl_surface versions prior
+ * to 5. See wl_surface.attach for details.
+ */
+static inline void
+wl_surface_offset(struct wl_surface *wl_surface, int32_t x, int32_t y)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_surface,
+			 WL_SURFACE_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) wl_surface), 0, x, y);
 }
 
 #ifndef WL_SEAT_CAPABILITY_ENUM
@@ -3931,6 +4080,22 @@
 };
 #endif /* WL_SEAT_CAPABILITY_ENUM */
 
+#ifndef WL_SEAT_ERROR_ENUM
+#define WL_SEAT_ERROR_ENUM
+/**
+ * @ingroup iface_wl_seat
+ * wl_seat error values
+ *
+ * These errors can be emitted in response to wl_seat requests.
+ */
+enum wl_seat_error {
+	/**
+	 * get_pointer, get_keyboard or get_touch called on seat without the matching capability
+	 */
+	WL_SEAT_ERROR_MISSING_CAPABILITY = 0,
+};
+#endif /* WL_SEAT_ERROR_ENUM */
+
 /**
  * @ingroup iface_wl_seat
  * @struct wl_seat_listener
@@ -3972,9 +4137,25 @@
 	/**
 	 * unique identifier for this seat
 	 *
-	 * In a multiseat configuration this can be used by the client to
-	 * help identify which physical devices the seat represents. Based
-	 * on the seat configuration used by the compositor.
+	 * In a multi-seat configuration the seat name can be used by
+	 * clients to help identify which physical devices the seat
+	 * represents.
+	 *
+	 * The seat name is a UTF-8 string with no convention defined for
+	 * its contents. Each name is unique among all wl_seat globals. The
+	 * name is only guaranteed to be unique for the current compositor
+	 * instance.
+	 *
+	 * The same seat names are used for all clients. Thus, the name can
+	 * be shared across processes to refer to a specific wl_seat
+	 * global.
+	 *
+	 * The name event is sent after binding to the seat global. This
+	 * event is only sent once per seat object, and the name does not
+	 * change over the lifetime of the wl_seat global.
+	 *
+	 * Compositors may re-use the same seat name if the wl_seat global
+	 * is destroyed and re-created later.
 	 * @param name seat identifier
 	 * @since 2
 	 */
@@ -4061,15 +4242,16 @@
  * This request only takes effect if the seat has the pointer
  * capability, or has had the pointer capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the pointer capability.
+ * never had the pointer capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_pointer *
 wl_seat_get_pointer(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_POINTER, &wl_pointer_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_POINTER, &wl_pointer_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_pointer *) id;
 }
@@ -4083,15 +4265,16 @@
  * This request only takes effect if the seat has the keyboard
  * capability, or has had the keyboard capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the keyboard capability.
+ * never had the keyboard capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_keyboard *
 wl_seat_get_keyboard(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_keyboard *) id;
 }
@@ -4105,15 +4288,16 @@
  * This request only takes effect if the seat has the touch
  * capability, or has had the touch capability in the past.
  * It is a protocol violation to issue this request on a seat that has
- * never had the touch capability.
+ * never had the touch capability. The missing_capability error will
+ * be sent in this case.
  */
 static inline struct wl_touch *
 wl_seat_get_touch(struct wl_seat *wl_seat)
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
-			 WL_SEAT_GET_TOUCH, &wl_touch_interface, NULL);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_GET_TOUCH, &wl_touch_interface, wl_proxy_get_version((struct wl_proxy *) wl_seat), 0, NULL);
 
 	return (struct wl_touch *) id;
 }
@@ -4127,10 +4311,8 @@
 static inline void
 wl_seat_release(struct wl_seat *wl_seat)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_seat,
-			 WL_SEAT_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_seat);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_seat,
+			 WL_SEAT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_seat), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_POINTER_ERROR_ENUM
@@ -4603,12 +4785,16 @@
  * wl_surface is no longer used as the cursor. When the use as a
  * cursor ends, the current and pending input regions become
  * undefined, and the wl_surface is unmapped.
+ *
+ * The serial parameter must match the latest wl_pointer.enter
+ * serial number sent to the client. Otherwise the request will be
+ * ignored.
  */
 static inline void
 wl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, int32_t hotspot_x, int32_t hotspot_y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_pointer,
-			 WL_POINTER_SET_CURSOR, serial, surface, hotspot_x, hotspot_y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer,
+			 WL_POINTER_SET_CURSOR, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), 0, serial, surface, hotspot_x, hotspot_y);
 }
 
 /**
@@ -4623,10 +4809,8 @@
 static inline void
 wl_pointer_release(struct wl_pointer *wl_pointer)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_pointer,
-			 WL_POINTER_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_pointer);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_pointer,
+			 WL_POINTER_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_pointer), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM
@@ -4679,7 +4863,8 @@
 	 * keyboard mapping
 	 *
 	 * This event provides a file descriptor to the client which can
-	 * be memory-mapped to provide a keyboard mapping description.
+	 * be memory-mapped in read-only mode to provide a keyboard mapping
+	 * description.
 	 *
 	 * From version 7 onwards, the fd must be mapped with MAP_PRIVATE
 	 * by the recipient, as MAP_SHARED may fail.
@@ -4697,6 +4882,9 @@
 	 *
 	 * Notification that this seat's keyboard focus is on a certain
 	 * surface.
+	 *
+	 * The compositor must send the wl_keyboard.modifiers event after
+	 * this event.
 	 * @param serial serial number of the enter event
 	 * @param surface surface gaining keyboard focus
 	 * @param keys the currently pressed keys
@@ -4714,6 +4902,10 @@
 	 *
 	 * The leave notification is sent before the enter notification for
 	 * the new focus.
+	 *
+	 * After this event client must assume that all keys, including
+	 * modifiers, are lifted and also it must stop key repeating if
+	 * there's some going on.
 	 * @param serial serial number of the leave event
 	 * @param surface surface that lost keyboard focus
 	 */
@@ -4726,6 +4918,12 @@
 	 *
 	 * A key was pressed or released. The time argument is a
 	 * timestamp with millisecond granularity, with an undefined base.
+	 *
+	 * The key is a platform-specific key code that can be interpreted
+	 * by feeding it to the keyboard mapping (see the keymap event).
+	 *
+	 * If this event produces a change in modifiers, then the resulting
+	 * wl_keyboard.modifiers event must be sent after this event.
 	 * @param serial serial number of the key event
 	 * @param time timestamp with millisecond granularity
 	 * @param key key that produced the event
@@ -4857,10 +5055,8 @@
 static inline void
 wl_keyboard_release(struct wl_keyboard *wl_keyboard)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_keyboard,
-			 WL_KEYBOARD_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_keyboard);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_keyboard,
+			 WL_KEYBOARD_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_keyboard), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5101,10 +5297,8 @@
 static inline void
 wl_touch_release(struct wl_touch *wl_touch)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_touch,
-			 WL_TOUCH_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_touch);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_touch,
+			 WL_TOUCH_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_touch), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #ifndef WL_OUTPUT_SUBPIXEL_ENUM
@@ -5234,13 +5428,16 @@
 	 * The physical size can be set to zero if it doesn't make sense
 	 * for this output (e.g. for projectors or virtual outputs).
 	 *
+	 * The geometry event will be followed by a done event (starting
+	 * from version 2).
+	 *
 	 * Note: wl_output only advertises partial information about the
 	 * output position and identification. Some compositors, for
 	 * instance those not implementing a desktop-style output layout or
 	 * those exposing virtual outputs, might fake this information.
 	 * Instead of using x and y, clients should use
 	 * xdg_output.logical_position. Instead of using make and model,
-	 * clients should use xdg_output.name and xdg_output.description.
+	 * clients should use name and description.
 	 * @param x x position within the global compositor space
 	 * @param y y position within the global compositor space
 	 * @param physical_width width in millimeters of the output
@@ -5271,6 +5468,10 @@
 	 * current. In other words, the current mode is always the last
 	 * mode that was received with the current flag set.
 	 *
+	 * Non-current modes are deprecated. A compositor can decide to
+	 * only advertise the current mode and never send other modes.
+	 * Clients should not rely on non-current modes.
+	 *
 	 * The size of a mode is given in physical hardware units of the
 	 * output device. This is not necessarily the same as the output
 	 * size in the global compositor space. For instance, the output
@@ -5282,6 +5483,9 @@
 	 * The vertical refresh rate can be set to zero if it doesn't make
 	 * sense for this output (e.g. for virtual outputs).
 	 *
+	 * The mode event will be followed by a done event (starting from
+	 * version 2).
+	 *
 	 * Clients should not use the refresh rate to schedule frames.
 	 * Instead, they should use the wl_surface.frame event or the
 	 * presentation-time protocol.
@@ -5331,12 +5535,77 @@
 	 * use wl_surface.set_buffer_scale with the scale of the output.
 	 * That way the compositor can avoid scaling the surface, and the
 	 * client can supply a higher detail image.
+	 *
+	 * The scale event will be followed by a done event.
 	 * @param factor scaling factor of output
 	 * @since 2
 	 */
 	void (*scale)(void *data,
 		      struct wl_output *wl_output,
 		      int32_t factor);
+	/**
+	 * name of this output
+	 *
+	 * Many compositors will assign user-friendly names to their
+	 * outputs, show them to the user, allow the user to refer to an
+	 * output, etc. The client may wish to know this name as well to
+	 * offer the user similar behaviors.
+	 *
+	 * The name is a UTF-8 string with no convention defined for its
+	 * contents. Each name is unique among all wl_output globals. The
+	 * name is only guaranteed to be unique for the compositor
+	 * instance.
+	 *
+	 * The same output name is used for all clients for a given
+	 * wl_output global. Thus, the name can be shared across processes
+	 * to refer to a specific wl_output global.
+	 *
+	 * The name is not guaranteed to be persistent across sessions,
+	 * thus cannot be used to reliably identify an output in e.g.
+	 * configuration files.
+	 *
+	 * Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc.
+	 * However, do not assume that the name is a reflection of an
+	 * underlying DRM connector, X11 connection, etc.
+	 *
+	 * The name event is sent after binding the output object. This
+	 * event is only sent once per output object, and the name does not
+	 * change over the lifetime of the wl_output global.
+	 *
+	 * Compositors may re-use the same output name if the wl_output
+	 * global is destroyed and re-created later. Compositors should
+	 * avoid re-using the same name if possible.
+	 *
+	 * The name event will be followed by a done event.
+	 * @param name output name
+	 * @since 4
+	 */
+	void (*name)(void *data,
+		     struct wl_output *wl_output,
+		     const char *name);
+	/**
+	 * human-readable description of this output
+	 *
+	 * Many compositors can produce human-readable descriptions of
+	 * their outputs. The client may wish to know this description as
+	 * well, e.g. for output selection purposes.
+	 *
+	 * The description is a UTF-8 string with no convention defined for
+	 * its contents. The description is not guaranteed to be unique
+	 * among all wl_output globals. Examples might include 'Foocorp 11"
+	 * Display' or 'Virtual X11 output via :1'.
+	 *
+	 * The description event is sent after binding the output object
+	 * and whenever the description changes. The description is
+	 * optional, and may not be sent at all.
+	 *
+	 * The description event will be followed by a done event.
+	 * @param description output description
+	 * @since 4
+	 */
+	void (*description)(void *data,
+			    struct wl_output *wl_output,
+			    const char *description);
 };
 
 /**
@@ -5368,6 +5637,14 @@
  * @ingroup iface_wl_output
  */
 #define WL_OUTPUT_SCALE_SINCE_VERSION 2
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_NAME_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_DESCRIPTION_SINCE_VERSION 4
 
 /**
  * @ingroup iface_wl_output
@@ -5410,10 +5687,8 @@
 static inline void
 wl_output_release(struct wl_output *wl_output)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_output,
-			 WL_OUTPUT_RELEASE);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_output);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_output,
+			 WL_OUTPUT_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_output), WL_MARSHAL_FLAG_DESTROY);
 }
 
 #define WL_REGION_DESTROY 0
@@ -5462,10 +5737,8 @@
 static inline void
 wl_region_destroy(struct wl_region *wl_region)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_region);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5476,8 +5749,8 @@
 static inline void
 wl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_ADD, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_ADD, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height);
 }
 
 /**
@@ -5488,8 +5761,8 @@
 static inline void
 wl_region_subtract(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_region,
-			 WL_REGION_SUBTRACT, x, y, width, height);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_region,
+			 WL_REGION_SUBTRACT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_region), 0, x, y, width, height);
 }
 
 #ifndef WL_SUBCOMPOSITOR_ERROR_ENUM
@@ -5545,10 +5818,8 @@
 static inline void
 wl_subcompositor_destroy(struct wl_subcompositor *wl_subcompositor)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subcompositor,
-			 WL_SUBCOMPOSITOR_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_subcompositor);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor,
+			 WL_SUBCOMPOSITOR_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5575,8 +5846,8 @@
 {
 	struct wl_proxy *id;
 
-	id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_subcompositor,
-			 WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, NULL, surface, parent);
+	id = wl_proxy_marshal_flags((struct wl_proxy *) wl_subcompositor,
+			 WL_SUBCOMPOSITOR_GET_SUBSURFACE, &wl_subsurface_interface, wl_proxy_get_version((struct wl_proxy *) wl_subcompositor), 0, NULL, surface, parent);
 
 	return (struct wl_subsurface *) id;
 }
@@ -5656,10 +5927,8 @@
 static inline void
 wl_subsurface_destroy(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_DESTROY);
-
-	wl_proxy_destroy((struct wl_proxy *) wl_subsurface);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), WL_MARSHAL_FLAG_DESTROY);
 }
 
 /**
@@ -5685,8 +5954,8 @@
 static inline void
 wl_subsurface_set_position(struct wl_subsurface *wl_subsurface, int32_t x, int32_t y)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_POSITION, x, y);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_POSITION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, x, y);
 }
 
 /**
@@ -5711,8 +5980,8 @@
 static inline void
 wl_subsurface_place_above(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_PLACE_ABOVE, sibling);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_PLACE_ABOVE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling);
 }
 
 /**
@@ -5724,8 +5993,8 @@
 static inline void
 wl_subsurface_place_below(struct wl_subsurface *wl_subsurface, struct wl_surface *sibling)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_PLACE_BELOW, sibling);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_PLACE_BELOW, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0, sibling);
 }
 
 /**
@@ -5748,8 +6017,8 @@
 static inline void
 wl_subsurface_set_sync(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_SYNC);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_SYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0);
 }
 
 /**
@@ -5778,8 +6047,8 @@
 static inline void
 wl_subsurface_set_desync(struct wl_subsurface *wl_subsurface)
 {
-	wl_proxy_marshal((struct wl_proxy *) wl_subsurface,
-			 WL_SUBSURFACE_SET_DESYNC);
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_subsurface,
+			 WL_SUBSURFACE_SET_DESYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0);
 }
 
 #ifdef  __cplusplus
diff --git a/third_party/wayland/include/protocol/wayland-server-protocol-core.h b/third_party/wayland/include/protocol/wayland-server-protocol-core.h
index 2372855..7554b75 100644
--- a/third_party/wayland/include/protocol/wayland-server-protocol-core.h
+++ b/third_party/wayland/include/protocol/wayland-server-protocol-core.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.18.0 */
+/* Generated by wayland-scanner 1.20.0 */
 
 #ifndef WAYLAND_SERVER_PROTOCOL_H
 #define WAYLAND_SERVER_PROTOCOL_H
@@ -91,6 +91,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -107,6 +109,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -159,6 +164,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -175,6 +183,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -193,6 +204,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -219,6 +233,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -249,15 +266,23 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  * @section page_iface_wl_buffer_api API
  * See @ref iface_wl_buffer.
  */
@@ -265,12 +290,20 @@
  * @defgroup iface_wl_buffer The wl_buffer interface
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -295,6 +328,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -315,6 +351,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -337,6 +376,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -369,6 +411,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -397,6 +442,9 @@
  * For desktop-style user interfaces, use xdg_shell.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -431,6 +479,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -525,6 +576,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -545,6 +599,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -573,6 +630,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -589,6 +649,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -617,6 +680,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -641,6 +707,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -661,6 +730,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -711,6 +783,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -736,7 +811,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -791,7 +866,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -821,6 +896,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
@@ -1145,6 +1221,9 @@
  * The drm format codes match the macros defined in drm_fourcc.h, except
  * argb8888 and xrgb8888. The formats actually supported by the compositor
  * will be reported by the format event.
+ *
+ * For all wl_shm formats and unless specified in another protocol
+ * extension, pre-multiplied alpha is used for pixel values.
  */
 enum wl_shm_format {
 	/**
@@ -1517,6 +1596,32 @@
 	 * 2x2 subsampled Cr:Cb plane 16 bits per channel
 	 */
 	WL_SHM_FORMAT_P016 = 0x36313050,
+	/**
+	 * [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
+	 */
+	WL_SHM_FORMAT_AXBXGXRX106106106106 = 0x30314241,
+	/**
+	 * 2x2 subsampled Cr:Cb plane
+	 */
+	WL_SHM_FORMAT_NV15 = 0x3531564e,
+	WL_SHM_FORMAT_Q410 = 0x30313451,
+	WL_SHM_FORMAT_Q401 = 0x31303451,
+	/**
+	 * [63:0] x:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XRGB16161616 = 0x38345258,
+	/**
+	 * [63:0] x:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XBGR16161616 = 0x38344258,
+	/**
+	 * [63:0] A:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ARGB16161616 = 0x38345241,
+	/**
+	 * [63:0] A:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ABGR16161616 = 0x38344241,
 };
 #endif /* WL_SHM_FORMAT_ENUM */
 
@@ -2032,7 +2137,8 @@
 	 * for the eventual data transfer. If source is NULL, enter, leave
 	 * and motion events are sent only to the client that initiated the
 	 * drag and the client is expected to handle the data passing
-	 * internally.
+	 * internally. If source is destroyed, the drag-and-drop session
+	 * will be cancelled.
 	 *
 	 * The origin surface is the surface where the drag originates and
 	 * the client must have an active implicit grab that matches the
@@ -2760,6 +2866,14 @@
 	 * buffer transform value is invalid
 	 */
 	WL_SURFACE_ERROR_INVALID_TRANSFORM = 1,
+	/**
+	 * buffer size is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_SIZE = 2,
+	/**
+	 * buffer offset is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_OFFSET = 3,
 };
 #endif /* WL_SURFACE_ERROR_ENUM */
 
@@ -2782,14 +2896,22 @@
 	 *
 	 * The new size of the surface is calculated based on the buffer
 	 * size transformed by the inverse buffer_transform and the inverse
-	 * buffer_scale. This means that the supplied buffer must be an
-	 * integer multiple of the buffer_scale.
+	 * buffer_scale. This means that at commit time the supplied buffer
+	 * size must be an integer multiple of the buffer_scale. If that's
+	 * not the case, an invalid_size error is sent.
 	 *
 	 * The x and y arguments specify the location of the new pending
 	 * buffer's upper left corner, relative to the current buffer's
 	 * upper left corner, in surface-local coordinates. In other words,
 	 * the x and y, combined with the new surface size define in which
-	 * directions the surface's size changes.
+	 * directions the surface's size changes. Setting anything other
+	 * than 0 as x and y arguments is discouraged, and should instead
+	 * be replaced with using the separate wl_surface.offset request.
+	 *
+	 * When the bound wl_surface version is 5 or higher, passing any
+	 * non-zero x or y is a protocol violation, and will result in an
+	 * 'invalid_offset' error being raised. To achieve equivalent
+	 * semantics, use wl_surface.offset.
 	 *
 	 * Surface contents are double-buffered state, see
 	 * wl_surface.commit.
@@ -2819,9 +2941,13 @@
 	 * storage or use wp_linux_buffer_release.
 	 *
 	 * Destroying the wl_buffer after wl_buffer.release does not change
-	 * the surface contents. However, if the client destroys the
-	 * wl_buffer before receiving the wl_buffer.release event, the
-	 * surface contents become undefined immediately.
+	 * the surface contents. Destroying the wl_buffer before
+	 * wl_buffer.release is allowed as long as the underlying buffer
+	 * storage isn't re-used (this can happen e.g. on client process
+	 * termination). However, if the client destroys the wl_buffer
+	 * before receiving the wl_buffer.release event and mutates the
+	 * underlying buffer storage, the surface contents become undefined
+	 * immediately.
 	 *
 	 * If wl_surface.attach is sent with a NULL wl_buffer, the
 	 * following wl_surface.commit will remove the surface content.
@@ -3124,6 +3250,29 @@
 			      int32_t y,
 			      int32_t width,
 			      int32_t height);
+	/**
+	 * set the surface contents offset
+	 *
+	 * The x and y arguments specify the location of the new pending
+	 * buffer's upper left corner, relative to the current buffer's
+	 * upper left corner, in surface-local coordinates. In other words,
+	 * the x and y, combined with the new surface size define in which
+	 * directions the surface's size changes.
+	 *
+	 * Surface location offset is double-buffered state, see
+	 * wl_surface.commit.
+	 *
+	 * This request is semantically equivalent to and the replaces the
+	 * x and y arguments in the wl_surface.attach request in wl_surface
+	 * versions prior to 5. See wl_surface.attach for details.
+	 * @param x surface-local x coordinate
+	 * @param y surface-local y coordinate
+	 * @since 5
+	 */
+	void (*offset)(struct wl_client *client,
+		       struct wl_resource *resource,
+		       int32_t x,
+		       int32_t y);
 };
 
 #define WL_SURFACE_ENTER 0
@@ -3178,6 +3327,10 @@
  * @ingroup iface_wl_surface
  */
 #define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_surface
+ */
+#define WL_SURFACE_OFFSET_SINCE_VERSION 5
 
 /**
  * @ingroup iface_wl_surface
@@ -3228,6 +3381,22 @@
 };
 #endif /* WL_SEAT_CAPABILITY_ENUM */
 
+#ifndef WL_SEAT_ERROR_ENUM
+#define WL_SEAT_ERROR_ENUM
+/**
+ * @ingroup iface_wl_seat
+ * wl_seat error values
+ *
+ * These errors can be emitted in response to wl_seat requests.
+ */
+enum wl_seat_error {
+	/**
+	 * get_pointer, get_keyboard or get_touch called on seat without the matching capability
+	 */
+	WL_SEAT_ERROR_MISSING_CAPABILITY = 0,
+};
+#endif /* WL_SEAT_ERROR_ENUM */
+
 /**
  * @ingroup iface_wl_seat
  * @struct wl_seat_interface
@@ -3242,7 +3411,8 @@
 	 * This request only takes effect if the seat has the pointer
 	 * capability, or has had the pointer capability in the past. It is
 	 * a protocol violation to issue this request on a seat that has
-	 * never had the pointer capability.
+	 * never had the pointer capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat pointer
 	 */
 	void (*get_pointer)(struct wl_client *client,
@@ -3257,7 +3427,8 @@
 	 * This request only takes effect if the seat has the keyboard
 	 * capability, or has had the keyboard capability in the past. It
 	 * is a protocol violation to issue this request on a seat that has
-	 * never had the keyboard capability.
+	 * never had the keyboard capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat keyboard
 	 */
 	void (*get_keyboard)(struct wl_client *client,
@@ -3272,7 +3443,8 @@
 	 * This request only takes effect if the seat has the touch
 	 * capability, or has had the touch capability in the past. It is a
 	 * protocol violation to issue this request on a seat that has
-	 * never had the touch capability.
+	 * never had the touch capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat touch interface
 	 */
 	void (*get_touch)(struct wl_client *client,
@@ -3480,6 +3652,10 @@
 	 * wl_surface is no longer used as the cursor. When the use as a
 	 * cursor ends, the current and pending input regions become
 	 * undefined, and the wl_surface is unmapped.
+	 *
+	 * The serial parameter must match the latest wl_pointer.enter
+	 * serial number sent to the client. Otherwise the request will be
+	 * ignored.
 	 * @param serial serial number of the enter event
 	 * @param surface pointer surface
 	 * @param hotspot_x surface-local x coordinate
@@ -4143,6 +4319,8 @@
 #define WL_OUTPUT_MODE 1
 #define WL_OUTPUT_DONE 2
 #define WL_OUTPUT_SCALE 3
+#define WL_OUTPUT_NAME 4
+#define WL_OUTPUT_DESCRIPTION 5
 
 /**
  * @ingroup iface_wl_output
@@ -4160,6 +4338,14 @@
  * @ingroup iface_wl_output
  */
 #define WL_OUTPUT_SCALE_SINCE_VERSION 2
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_NAME_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_DESCRIPTION_SINCE_VERSION 4
 
 /**
  * @ingroup iface_wl_output
@@ -4224,6 +4410,30 @@
 }
 
 /**
+ * @ingroup iface_wl_output
+ * Sends an name event to the client owning the resource.
+ * @param resource_ The client's resource
+ * @param name output name
+ */
+static inline void
+wl_output_send_name(struct wl_resource *resource_, const char *name)
+{
+	wl_resource_post_event(resource_, WL_OUTPUT_NAME, name);
+}
+
+/**
+ * @ingroup iface_wl_output
+ * Sends an description event to the client owning the resource.
+ * @param resource_ The client's resource
+ * @param description output description
+ */
+static inline void
+wl_output_send_description(struct wl_resource *resource_, const char *description)
+{
+	wl_resource_post_event(resource_, WL_OUTPUT_DESCRIPTION, description);
+}
+
+/**
  * @ingroup iface_wl_region
  * @struct wl_region_interface
  */
diff --git a/third_party/wayland/include/protocol/wayland-server-protocol.h b/third_party/wayland/include/protocol/wayland-server-protocol.h
index 3a2e92d6..d4f8952 100644
--- a/third_party/wayland/include/protocol/wayland-server-protocol.h
+++ b/third_party/wayland/include/protocol/wayland-server-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.18.0 */
+/* Generated by wayland-scanner 1.20.0 */
 
 #ifndef WAYLAND_SERVER_PROTOCOL_H
 #define WAYLAND_SERVER_PROTOCOL_H
@@ -91,6 +91,8 @@
 struct wl_surface;
 struct wl_touch;
 
+#ifndef WL_DISPLAY_INTERFACE
+#define WL_DISPLAY_INTERFACE
 /**
  * @page page_iface_wl_display wl_display
  * @section page_iface_wl_display_desc Description
@@ -107,6 +109,9 @@
  * is used for internal Wayland protocol features.
  */
 extern const struct wl_interface wl_display_interface;
+#endif
+#ifndef WL_REGISTRY_INTERFACE
+#define WL_REGISTRY_INTERFACE
 /**
  * @page page_iface_wl_registry wl_registry
  * @section page_iface_wl_registry_desc Description
@@ -159,6 +164,9 @@
  * the object.
  */
 extern const struct wl_interface wl_registry_interface;
+#endif
+#ifndef WL_CALLBACK_INTERFACE
+#define WL_CALLBACK_INTERFACE
 /**
  * @page page_iface_wl_callback wl_callback
  * @section page_iface_wl_callback_desc Description
@@ -175,6 +183,9 @@
  * the related request is done.
  */
 extern const struct wl_interface wl_callback_interface;
+#endif
+#ifndef WL_COMPOSITOR_INTERFACE
+#define WL_COMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_compositor wl_compositor
  * @section page_iface_wl_compositor_desc Description
@@ -193,6 +204,9 @@
  * surfaces into one displayable output.
  */
 extern const struct wl_interface wl_compositor_interface;
+#endif
+#ifndef WL_SHM_POOL_INTERFACE
+#define WL_SHM_POOL_INTERFACE
 /**
  * @page page_iface_wl_shm_pool wl_shm_pool
  * @section page_iface_wl_shm_pool_desc Description
@@ -219,6 +233,9 @@
  * a surface or for many small buffers.
  */
 extern const struct wl_interface wl_shm_pool_interface;
+#endif
+#ifndef WL_SHM_INTERFACE
+#define WL_SHM_INTERFACE
 /**
  * @page page_iface_wl_shm wl_shm
  * @section page_iface_wl_shm_desc Description
@@ -249,15 +266,23 @@
  * that can be used for buffers.
  */
 extern const struct wl_interface wl_shm_interface;
+#endif
+#ifndef WL_BUFFER_INTERFACE
+#define WL_BUFFER_INTERFACE
 /**
  * @page page_iface_wl_buffer wl_buffer
  * @section page_iface_wl_buffer_desc Description
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  * @section page_iface_wl_buffer_api API
  * See @ref iface_wl_buffer.
  */
@@ -265,12 +290,20 @@
  * @defgroup iface_wl_buffer The wl_buffer interface
  *
  * A buffer provides the content for a wl_surface. Buffers are
- * created through factory interfaces such as wl_drm, wl_shm or
- * similar. It has a width and a height and can be attached to a
- * wl_surface, but the mechanism by which a client provides and
- * updates the contents is defined by the buffer factory interface.
+ * created through factory interfaces such as wl_shm, wp_linux_buffer_params
+ * (from the linux-dmabuf protocol extension) or similar. It has a width and
+ * a height and can be attached to a wl_surface, but the mechanism by which a
+ * client provides and updates the contents is defined by the buffer factory
+ * interface.
+ *
+ * If the buffer uses a format that has an alpha channel, the alpha channel
+ * is assumed to be premultiplied in the color channels unless otherwise
+ * specified.
  */
 extern const struct wl_interface wl_buffer_interface;
+#endif
+#ifndef WL_DATA_OFFER_INTERFACE
+#define WL_DATA_OFFER_INTERFACE
 /**
  * @page page_iface_wl_data_offer wl_data_offer
  * @section page_iface_wl_data_offer_desc Description
@@ -295,6 +328,9 @@
  * data directly from the source client.
  */
 extern const struct wl_interface wl_data_offer_interface;
+#endif
+#ifndef WL_DATA_SOURCE_INTERFACE
+#define WL_DATA_SOURCE_INTERFACE
 /**
  * @page page_iface_wl_data_source wl_data_source
  * @section page_iface_wl_data_source_desc Description
@@ -315,6 +351,9 @@
  * to requests to transfer the data.
  */
 extern const struct wl_interface wl_data_source_interface;
+#endif
+#ifndef WL_DATA_DEVICE_INTERFACE
+#define WL_DATA_DEVICE_INTERFACE
 /**
  * @page page_iface_wl_data_device wl_data_device
  * @section page_iface_wl_data_device_desc Description
@@ -337,6 +376,9 @@
  * mechanisms such as copy-and-paste and drag-and-drop.
  */
 extern const struct wl_interface wl_data_device_interface;
+#endif
+#ifndef WL_DATA_DEVICE_MANAGER_INTERFACE
+#define WL_DATA_DEVICE_MANAGER_INTERFACE
 /**
  * @page page_iface_wl_data_device_manager wl_data_device_manager
  * @section page_iface_wl_data_device_manager_desc Description
@@ -369,6 +411,9 @@
  * wl_data_offer.accept and wl_data_offer.finish for details.
  */
 extern const struct wl_interface wl_data_device_manager_interface;
+#endif
+#ifndef WL_SHELL_INTERFACE
+#define WL_SHELL_INTERFACE
 /**
  * @page page_iface_wl_shell wl_shell
  * @section page_iface_wl_shell_desc Description
@@ -397,6 +442,9 @@
  * For desktop-style user interfaces, use xdg_shell.
  */
 extern const struct wl_interface wl_shell_interface;
+#endif
+#ifndef WL_SHELL_SURFACE_INTERFACE
+#define WL_SHELL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_shell_surface wl_shell_surface
  * @section page_iface_wl_shell_surface_desc Description
@@ -431,6 +479,9 @@
  * the wl_surface object.
  */
 extern const struct wl_interface wl_shell_surface_interface;
+#endif
+#ifndef WL_SURFACE_INTERFACE
+#define WL_SURFACE_INTERFACE
 /**
  * @page page_iface_wl_surface wl_surface
  * @section page_iface_wl_surface_desc Description
@@ -525,6 +576,9 @@
  * switching is not allowed).
  */
 extern const struct wl_interface wl_surface_interface;
+#endif
+#ifndef WL_SEAT_INTERFACE
+#define WL_SEAT_INTERFACE
 /**
  * @page page_iface_wl_seat wl_seat
  * @section page_iface_wl_seat_desc Description
@@ -545,6 +599,9 @@
  * maintains a keyboard focus and a pointer focus.
  */
 extern const struct wl_interface wl_seat_interface;
+#endif
+#ifndef WL_POINTER_INTERFACE
+#define WL_POINTER_INTERFACE
 /**
  * @page page_iface_wl_pointer wl_pointer
  * @section page_iface_wl_pointer_desc Description
@@ -573,6 +630,9 @@
  * and scrolling.
  */
 extern const struct wl_interface wl_pointer_interface;
+#endif
+#ifndef WL_KEYBOARD_INTERFACE
+#define WL_KEYBOARD_INTERFACE
 /**
  * @page page_iface_wl_keyboard wl_keyboard
  * @section page_iface_wl_keyboard_desc Description
@@ -589,6 +649,9 @@
  * associated with a seat.
  */
 extern const struct wl_interface wl_keyboard_interface;
+#endif
+#ifndef WL_TOUCH_INTERFACE
+#define WL_TOUCH_INTERFACE
 /**
  * @page page_iface_wl_touch wl_touch
  * @section page_iface_wl_touch_desc Description
@@ -617,6 +680,9 @@
  * contact point can be identified by the ID of the sequence.
  */
 extern const struct wl_interface wl_touch_interface;
+#endif
+#ifndef WL_OUTPUT_INTERFACE
+#define WL_OUTPUT_INTERFACE
 /**
  * @page page_iface_wl_output wl_output
  * @section page_iface_wl_output_desc Description
@@ -641,6 +707,9 @@
  * as global during start up, or when a monitor is hotplugged.
  */
 extern const struct wl_interface wl_output_interface;
+#endif
+#ifndef WL_REGION_INTERFACE
+#define WL_REGION_INTERFACE
 /**
  * @page page_iface_wl_region wl_region
  * @section page_iface_wl_region_desc Description
@@ -661,6 +730,9 @@
  * regions of a surface.
  */
 extern const struct wl_interface wl_region_interface;
+#endif
+#ifndef WL_SUBCOMPOSITOR_INTERFACE
+#define WL_SUBCOMPOSITOR_INTERFACE
 /**
  * @page page_iface_wl_subcompositor wl_subcompositor
  * @section page_iface_wl_subcompositor_desc Description
@@ -711,6 +783,9 @@
  * processing to dedicated overlay hardware when possible.
  */
 extern const struct wl_interface wl_subcompositor_interface;
+#endif
+#ifndef WL_SUBSURFACE_INTERFACE
+#define WL_SUBSURFACE_INTERFACE
 /**
  * @page page_iface_wl_subsurface wl_subsurface
  * @section page_iface_wl_subsurface_desc Description
@@ -736,7 +811,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -791,7 +866,7 @@
  * wl_surface state directly. A sub-surface is initially in the
  * synchronized mode.
  *
- * Sub-surfaces have also other kind of state, which is managed by
+ * Sub-surfaces also have another kind of state, which is managed by
  * wl_subsurface requests, as opposed to wl_surface requests. This
  * state includes the sub-surface position relative to the parent
  * surface (wl_subsurface.set_position), and the stacking order of
@@ -821,6 +896,7 @@
  * unmapped.
  */
 extern const struct wl_interface wl_subsurface_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
@@ -1145,6 +1221,9 @@
  * The drm format codes match the macros defined in drm_fourcc.h, except
  * argb8888 and xrgb8888. The formats actually supported by the compositor
  * will be reported by the format event.
+ *
+ * For all wl_shm formats and unless specified in another protocol
+ * extension, pre-multiplied alpha is used for pixel values.
  */
 enum wl_shm_format {
 	/**
@@ -1517,6 +1596,32 @@
 	 * 2x2 subsampled Cr:Cb plane 16 bits per channel
 	 */
 	WL_SHM_FORMAT_P016 = 0x36313050,
+	/**
+	 * [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
+	 */
+	WL_SHM_FORMAT_AXBXGXRX106106106106 = 0x30314241,
+	/**
+	 * 2x2 subsampled Cr:Cb plane
+	 */
+	WL_SHM_FORMAT_NV15 = 0x3531564e,
+	WL_SHM_FORMAT_Q410 = 0x30313451,
+	WL_SHM_FORMAT_Q401 = 0x31303451,
+	/**
+	 * [63:0] x:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XRGB16161616 = 0x38345258,
+	/**
+	 * [63:0] x:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_XBGR16161616 = 0x38344258,
+	/**
+	 * [63:0] A:R:G:B 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ARGB16161616 = 0x38345241,
+	/**
+	 * [63:0] A:B:G:R 16:16:16:16 little endian
+	 */
+	WL_SHM_FORMAT_ABGR16161616 = 0x38344241,
 };
 #endif /* WL_SHM_FORMAT_ENUM */
 
@@ -2032,7 +2137,8 @@
 	 * for the eventual data transfer. If source is NULL, enter, leave
 	 * and motion events are sent only to the client that initiated the
 	 * drag and the client is expected to handle the data passing
-	 * internally.
+	 * internally. If source is destroyed, the drag-and-drop session
+	 * will be cancelled.
 	 *
 	 * The origin surface is the surface where the drag originates and
 	 * the client must have an active implicit grab that matches the
@@ -2760,6 +2866,14 @@
 	 * buffer transform value is invalid
 	 */
 	WL_SURFACE_ERROR_INVALID_TRANSFORM = 1,
+	/**
+	 * buffer size is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_SIZE = 2,
+	/**
+	 * buffer offset is invalid
+	 */
+	WL_SURFACE_ERROR_INVALID_OFFSET = 3,
 };
 #endif /* WL_SURFACE_ERROR_ENUM */
 
@@ -2782,14 +2896,22 @@
 	 *
 	 * The new size of the surface is calculated based on the buffer
 	 * size transformed by the inverse buffer_transform and the inverse
-	 * buffer_scale. This means that the supplied buffer must be an
-	 * integer multiple of the buffer_scale.
+	 * buffer_scale. This means that at commit time the supplied buffer
+	 * size must be an integer multiple of the buffer_scale. If that's
+	 * not the case, an invalid_size error is sent.
 	 *
 	 * The x and y arguments specify the location of the new pending
 	 * buffer's upper left corner, relative to the current buffer's
 	 * upper left corner, in surface-local coordinates. In other words,
 	 * the x and y, combined with the new surface size define in which
-	 * directions the surface's size changes.
+	 * directions the surface's size changes. Setting anything other
+	 * than 0 as x and y arguments is discouraged, and should instead
+	 * be replaced with using the separate wl_surface.offset request.
+	 *
+	 * When the bound wl_surface version is 5 or higher, passing any
+	 * non-zero x or y is a protocol violation, and will result in an
+	 * 'invalid_offset' error being raised. To achieve equivalent
+	 * semantics, use wl_surface.offset.
 	 *
 	 * Surface contents are double-buffered state, see
 	 * wl_surface.commit.
@@ -2819,9 +2941,13 @@
 	 * storage or use wp_linux_buffer_release.
 	 *
 	 * Destroying the wl_buffer after wl_buffer.release does not change
-	 * the surface contents. However, if the client destroys the
-	 * wl_buffer before receiving the wl_buffer.release event, the
-	 * surface contents become undefined immediately.
+	 * the surface contents. Destroying the wl_buffer before
+	 * wl_buffer.release is allowed as long as the underlying buffer
+	 * storage isn't re-used (this can happen e.g. on client process
+	 * termination). However, if the client destroys the wl_buffer
+	 * before receiving the wl_buffer.release event and mutates the
+	 * underlying buffer storage, the surface contents become undefined
+	 * immediately.
 	 *
 	 * If wl_surface.attach is sent with a NULL wl_buffer, the
 	 * following wl_surface.commit will remove the surface content.
@@ -3124,6 +3250,29 @@
 			      int32_t y,
 			      int32_t width,
 			      int32_t height);
+	/**
+	 * set the surface contents offset
+	 *
+	 * The x and y arguments specify the location of the new pending
+	 * buffer's upper left corner, relative to the current buffer's
+	 * upper left corner, in surface-local coordinates. In other words,
+	 * the x and y, combined with the new surface size define in which
+	 * directions the surface's size changes.
+	 *
+	 * Surface location offset is double-buffered state, see
+	 * wl_surface.commit.
+	 *
+	 * This request is semantically equivalent to and the replaces the
+	 * x and y arguments in the wl_surface.attach request in wl_surface
+	 * versions prior to 5. See wl_surface.attach for details.
+	 * @param x surface-local x coordinate
+	 * @param y surface-local y coordinate
+	 * @since 5
+	 */
+	void (*offset)(struct wl_client *client,
+		       struct wl_resource *resource,
+		       int32_t x,
+		       int32_t y);
 };
 
 #define WL_SURFACE_ENTER 0
@@ -3178,6 +3327,10 @@
  * @ingroup iface_wl_surface
  */
 #define WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_surface
+ */
+#define WL_SURFACE_OFFSET_SINCE_VERSION 5
 
 /**
  * @ingroup iface_wl_surface
@@ -3228,6 +3381,22 @@
 };
 #endif /* WL_SEAT_CAPABILITY_ENUM */
 
+#ifndef WL_SEAT_ERROR_ENUM
+#define WL_SEAT_ERROR_ENUM
+/**
+ * @ingroup iface_wl_seat
+ * wl_seat error values
+ *
+ * These errors can be emitted in response to wl_seat requests.
+ */
+enum wl_seat_error {
+	/**
+	 * get_pointer, get_keyboard or get_touch called on seat without the matching capability
+	 */
+	WL_SEAT_ERROR_MISSING_CAPABILITY = 0,
+};
+#endif /* WL_SEAT_ERROR_ENUM */
+
 /**
  * @ingroup iface_wl_seat
  * @struct wl_seat_interface
@@ -3242,7 +3411,8 @@
 	 * This request only takes effect if the seat has the pointer
 	 * capability, or has had the pointer capability in the past. It is
 	 * a protocol violation to issue this request on a seat that has
-	 * never had the pointer capability.
+	 * never had the pointer capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat pointer
 	 */
 	void (*get_pointer)(struct wl_client *client,
@@ -3257,7 +3427,8 @@
 	 * This request only takes effect if the seat has the keyboard
 	 * capability, or has had the keyboard capability in the past. It
 	 * is a protocol violation to issue this request on a seat that has
-	 * never had the keyboard capability.
+	 * never had the keyboard capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat keyboard
 	 */
 	void (*get_keyboard)(struct wl_client *client,
@@ -3272,7 +3443,8 @@
 	 * This request only takes effect if the seat has the touch
 	 * capability, or has had the touch capability in the past. It is a
 	 * protocol violation to issue this request on a seat that has
-	 * never had the touch capability.
+	 * never had the touch capability. The missing_capability error
+	 * will be sent in this case.
 	 * @param id seat touch interface
 	 */
 	void (*get_touch)(struct wl_client *client,
@@ -3480,6 +3652,10 @@
 	 * wl_surface is no longer used as the cursor. When the use as a
 	 * cursor ends, the current and pending input regions become
 	 * undefined, and the wl_surface is unmapped.
+	 *
+	 * The serial parameter must match the latest wl_pointer.enter
+	 * serial number sent to the client. Otherwise the request will be
+	 * ignored.
 	 * @param serial serial number of the enter event
 	 * @param surface pointer surface
 	 * @param hotspot_x surface-local x coordinate
@@ -4143,6 +4319,8 @@
 #define WL_OUTPUT_MODE 1
 #define WL_OUTPUT_DONE 2
 #define WL_OUTPUT_SCALE 3
+#define WL_OUTPUT_NAME 4
+#define WL_OUTPUT_DESCRIPTION 5
 
 /**
  * @ingroup iface_wl_output
@@ -4160,6 +4338,14 @@
  * @ingroup iface_wl_output
  */
 #define WL_OUTPUT_SCALE_SINCE_VERSION 2
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_NAME_SINCE_VERSION 4
+/**
+ * @ingroup iface_wl_output
+ */
+#define WL_OUTPUT_DESCRIPTION_SINCE_VERSION 4
 
 /**
  * @ingroup iface_wl_output
@@ -4224,6 +4410,30 @@
 }
 
 /**
+ * @ingroup iface_wl_output
+ * Sends an name event to the client owning the resource.
+ * @param resource_ The client's resource
+ * @param name output name
+ */
+static inline void
+wl_output_send_name(struct wl_resource *resource_, const char *name)
+{
+	wl_resource_post_event(resource_, WL_OUTPUT_NAME, name);
+}
+
+/**
+ * @ingroup iface_wl_output
+ * Sends an description event to the client owning the resource.
+ * @param resource_ The client's resource
+ * @param description output description
+ */
+static inline void
+wl_output_send_description(struct wl_resource *resource_, const char *description)
+{
+	wl_resource_post_event(resource_, WL_OUTPUT_DESCRIPTION, description);
+}
+
+/**
  * @ingroup iface_wl_region
  * @struct wl_region_interface
  */
diff --git a/third_party/wayland/include/src/wayland-version.h b/third_party/wayland/include/src/wayland-version.h
index 06322dad..d118d54 100644
--- a/third_party/wayland/include/src/wayland-version.h
+++ b/third_party/wayland/include/src/wayland-version.h
@@ -27,8 +27,8 @@
 #define WAYLAND_VERSION_H
 
 #define WAYLAND_VERSION_MAJOR 1
-#define WAYLAND_VERSION_MINOR 18
+#define WAYLAND_VERSION_MINOR 20
 #define WAYLAND_VERSION_MICRO 0
-#define WAYLAND_VERSION "1.18.0"
+#define WAYLAND_VERSION "1.20.0"
 
 #endif
diff --git a/third_party/wayland/protocol/wayland-protocol.c b/third_party/wayland/protocol/wayland-protocol.c
index 23043e1..a2254d219 100644
--- a/third_party/wayland/protocol/wayland-protocol.c
+++ b/third_party/wayland/protocol/wayland-protocol.c
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.18.0 */
+/* Generated by wayland-scanner 1.20.0 */
 
 /*
  * Copyright © 2008-2011 Kristian Høgsberg
@@ -193,7 +193,7 @@
 };
 
 WL_EXPORT const struct wl_interface wl_compositor_interface = {
-	"wl_compositor", 4,
+	"wl_compositor", 5,
 	2, wl_compositor_requests,
 	0, NULL,
 };
@@ -357,6 +357,7 @@
 	{ "set_buffer_transform", "2i", wayland_types + 0 },
 	{ "set_buffer_scale", "3i", wayland_types + 0 },
 	{ "damage_buffer", "4iiii", wayland_types + 0 },
+	{ "offset", "5ii", wayland_types + 0 },
 };
 
 static const struct wl_message wl_surface_events[] = {
@@ -365,8 +366,8 @@
 };
 
 WL_EXPORT const struct wl_interface wl_surface_interface = {
-	"wl_surface", 4,
-	10, wl_surface_requests,
+	"wl_surface", 5,
+	11, wl_surface_requests,
 	2, wl_surface_events,
 };
 
@@ -459,12 +460,14 @@
 	{ "mode", "uiii", wayland_types + 0 },
 	{ "done", "2", wayland_types + 0 },
 	{ "scale", "2i", wayland_types + 0 },
+	{ "name", "4s", wayland_types + 0 },
+	{ "description", "4s", wayland_types + 0 },
 };
 
 WL_EXPORT const struct wl_interface wl_output_interface = {
-	"wl_output", 3,
+	"wl_output", 4,
 	1, wl_output_requests,
-	4, wl_output_events,
+	6, wl_output_events,
 };
 
 static const struct wl_message wl_region_requests[] = {
diff --git a/third_party/wayland/stubs/libwayland-client.sigs b/third_party/wayland/stubs/libwayland-client.sigs
index c9689fe8..cee3939 100644
--- a/third_party/wayland/stubs/libwayland-client.sigs
+++ b/third_party/wayland/stubs/libwayland-client.sigs
@@ -1,6 +1,7 @@
 // These signatures are obtained by hand from wayland-client-core.h.
 
 void wl_event_queue_destroy(struct wl_event_queue* queue);
+struct wl_proxy* wl_proxy_marshal_array_flags(struct wl_proxy* proxy, uint32_t opcode, const struct wl_interface* interface, uint32_t version, uint32_t flags, union wl_argument* args);
 void wl_proxy_marshal_array(struct wl_proxy* p, uint32_t opcode, union wl_argument* args);
 struct wl_proxy* wl_proxy_create(struct wl_proxy* factory, const struct wl_interface* interface);
 void* wl_proxy_create_wrapper(void* proxy);
@@ -42,6 +43,7 @@
 // void wl_proxy_marshal(struct wl_proxy* p, uint32_t opcode, ...);
 // struct wl_proxy* wl_proxy_marshal_constructor(struct wl_proxy* proxy, uint32_t opcode, const struct wl_interface* interface, ...);
 // struct wl_proxy* wl_proxy_marshal_constructor_versioned(struct wl_proxy* proxy, uint32_t opcode, const struct wl_interface* interface, uint32_t version, ...);
+// struct wl_proxy* wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...);
 
 // These signatures are obtained by hand from wayland-util.h.
 // Wayland utility is not a separate library, but rather included in libwayland-* libraries.
diff --git a/third_party/wayland/stubs/libwayland_variadic_support.cc b/third_party/wayland/stubs/libwayland_variadic_support.cc
index 8f7d35e6..242573f 100644
--- a/third_party/wayland/stubs/libwayland_variadic_support.cc
+++ b/third_party/wayland/stubs/libwayland_variadic_support.cc
@@ -13,6 +13,12 @@
 #include <cstdarg>
 #include <cstdint>
 
+#define CHROME_WAYLAND_CHECK_VERSION(x, y, z)                   \
+  (WAYLAND_VERSION_MAJOR > x ||                                 \
+   (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR > y) || \
+   (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR == y && \
+    WAYLAND_VERSION_MICRO >= z))
+
 #define WL_CLOSURE_MAX_ARGS 20
 
 extern "C" {
@@ -136,4 +142,25 @@
   return wl_proxy_marshal_array_constructor_versioned(proxy, opcode, args,
                                                       interface, version);
 }
+
+#if CHROME_WAYLAND_CHECK_VERSION(1, 20, 0)
+struct wl_proxy* wl_proxy_marshal_flags(struct wl_proxy* proxy,
+                                        uint32_t opcode,
+                                        const struct wl_interface* interface,
+                                        uint32_t version,
+                                        uint32_t flags,
+                                        ...) {
+  union wl_argument args[WL_CLOSURE_MAX_ARGS];
+  va_list ap;
+
+  va_start(ap, flags);
+  wl_argument_from_va_list(
+      reinterpret_cast<wl_object*>(proxy)->interface->methods[opcode].signature,
+      args, WL_CLOSURE_MAX_ARGS, ap);
+  va_end(ap);
+
+  return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, flags,
+                                      args);
+}
+#endif
 }
diff --git a/third_party/wayland/wayland_protocol.gni b/third_party/wayland/wayland_protocol.gni
index 080996f..308f099 100644
--- a/third_party/wayland/wayland_protocol.gni
+++ b/third_party/wayland/wayland_protocol.gni
@@ -55,7 +55,7 @@
     if (use_system_wayland_scanner) {
       args += [
         "--cmd",
-        system_wayland_scanner_path,
+        "./" + rebase_path(system_wayland_scanner_path, root_build_dir),
       ]
     } else {
       wayland_scanner_label =
@@ -63,10 +63,9 @@
       deps = [ wayland_scanner_label ]
       wayland_scanner_path = get_label_info(wayland_scanner_label,
                                             "root_out_dir") + "/wayland_scanner"
-      cmd = "./" + rebase_path(wayland_scanner_path, root_build_dir)
       args += [
         "--cmd",
-        cmd,
+        "./" + rebase_path(wayland_scanner_path, root_build_dir),
       ]
     }
   }
diff --git a/third_party/weston/BUILD.gn b/third_party/weston/BUILD.gn
index 1f81128..3cd83de 100644
--- a/third_party/weston/BUILD.gn
+++ b/third_party/weston/BUILD.gn
@@ -62,6 +62,10 @@
   ]
 
   ldflags = [ "-Wl,-rpath=\$ORIGIN" ]
+
+  if (!use_system_libwayland) {
+    configs = [ "//third_party/wayland:wayland_config" ]
+  }
 }
 
 pkg_config("pixman") {
diff --git a/third_party/weston/generate_configs.py b/third_party/weston/generate_configs.py
index cbe85b05..b3623b5a 100755
--- a/third_party/weston/generate_configs.py
+++ b/third_party/weston/generate_configs.py
@@ -8,6 +8,7 @@
 from __future__ import print_function
 
 import os
+import pathlib
 import re
 import shutil
 import subprocess
@@ -49,6 +50,10 @@
 ]
 
 
+def GetAbsPath(relative_path):
+    return os.path.join(BASE_DIR, relative_path)
+
+
 def PrintAndCheckCall(argv, *args, **kwargs):
     print('\n-------------------------------------------------\nRunning %s' %
           ' '.join(argv))
@@ -100,7 +105,7 @@
     temp_dir = tempfile.mkdtemp()
     PrintAndCheckCall(
         MESON + DEFAULT_BUILD_ARGS + special_args + [temp_dir],
-        cwd='src',
+        cwd=GetAbsPath('src'),
         env=env)
 
     label = subprocess.check_output(["git", "describe", "--always"]).strip()
@@ -115,14 +120,14 @@
     temp_dir = tempfile.mkdtemp()
     PrintAndCheckCall(
         MESON + DEFAULT_BUILD_ARGS + special_args + [temp_dir],
-        cwd='src',
+        cwd=GetAbsPath('src'),
         env=env)
 
     CopyConfigsAndCleanup(temp_dir, config_dir)
 
 
 def ChangeConfigPath():
-    configfile = os.path.join(BASE_DIR, "config/config.h")
+    configfile = GetAbsPath("config/config.h")
     DIRS = ["BINDIR",
             "DATADIR",
             "LIBEXECDIR",
@@ -137,12 +142,12 @@
 
 
 def GenerateWestonVersion():
-    dirname = os.path.join(BASE_DIR, "version/libweston")
+    dirname = GetAbsPath("version/libweston")
     if not os.path.exists(dirname):
         os.makedirs(dirname)
-    version_op_file = os.path.join(BASE_DIR, "version/libweston/version.h")
-    configfile = os.path.join(BASE_DIR, "config/config.h")
-    version_in_file = os.path.join(BASE_DIR, "src/include/libweston/version.h.in")
+    version_op_file = GetAbsPath("version/libweston/version.h")
+    configfile = GetAbsPath("config/config.h")
+    version_in_file = GetAbsPath("src/include/libweston/version.h.in")
     version_number = "0.0.0"
     with open(configfile, 'r') as f:
         for line in f:
@@ -169,7 +174,7 @@
 
 
 def RemoveUndesiredDefines():
-    configfile = os.path.join(BASE_DIR, "config/config.h")
+    configfile = GetAbsPath('config/config.h')
     # Weston doesn't have a meson option to avoid using memfd_create() method that was
     # introduced in GLIBC 2.27. That results in weston failing to run on Xenial based bot as
     # it has GLIBC 2.23, because this config might be generated on a system that has newer
@@ -181,8 +186,8 @@
 def main():
     env = os.environ
     env['CC'] = 'clang'
-    GenerateGitConfig('version', env)
-    GenerateConfig('config', env)
+    GenerateGitConfig(GetAbsPath('version'), env)
+    GenerateConfig(GetAbsPath('config'), env)
     ChangeConfigPath()
     RemoveUndesiredDefines()
     GenerateWestonVersion()
diff --git a/third_party/weston/version/git-version.h b/third_party/weston/version/git-version.h
index a7494f70..20c4637 100644
--- a/third_party/weston/version/git-version.h
+++ b/third_party/weston/version/git-version.h
@@ -1 +1 @@
-#define BUILD_ID "fc0cbffa4e29"
+#define BUILD_ID "c3259c0e807e"
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index 7a4e32f8..5fa1c052 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -135,6 +135,7 @@
     "ui/base/test/ui_base_test_resources.grd": "Test data",
     "ui/strings/app_locale_settings.grd": "Not UI strings; localized separately",
     "ui/views/examples/views_examples_resources.grd": "Test data",
+    "ui/webui/examples/resources/webui_examples_resources.grd": "Test data",
   },
   # Internal grds that contain parts not available publicly and thus should
   # not be checked by translation script.
diff --git a/tools/mac/power/driver_scripts_templates/navigation b/tools/mac/power/driver_scripts_templates/navigation
index 94f57ead..891b9e5 100644
--- a/tools/mac/power/driver_scripts_templates/navigation
+++ b/tools/mac/power/driver_scripts_templates/navigation
@@ -21,25 +21,20 @@
       activate
     end if
 
+    set w to first window
+
     if it is running then
       activate
 
       {{ make_maximized() }}
       set sites to { {{ sites }} }
 
-      -- Ensure a tab stays live all the time so the window doesn't go away.
-      open location "about:blank"
-
-      -- Each cycles takes about 2 mins. Aim for a test that takes an hour.
       repeat with i from 1 to {{ navigation_cycles }}
         repeat with site in sites
-          open location site
+          set URL of active tab of w to site
 
           delay {{ per_navigation_delay }}
 
-          tell active tab of w
-            close
-          end tell
         end repeat
 
       end repeat
diff --git a/tools/mac/power/driver_scripts_templates/safari_navigation b/tools/mac/power/driver_scripts_templates/safari_navigation
index 86ce8a2..0cb367bf 100644
--- a/tools/mac/power/driver_scripts_templates/safari_navigation
+++ b/tools/mac/power/driver_scripts_templates/safari_navigation
@@ -23,15 +23,12 @@
     {{ make_maximized() }}
     set sites to { {{ sites }} }
 
-    -- Each cycles takes about 2 mins. Aim for a test that takes an hour.
     repeat with i from 1 to {{ navigation_cycles }}
       repeat with site in sites
-        open location site
+        tell application "Safari" to set the URL of the front document to site
 
         delay {{ per_navigation_delay }}
 
-        set t to current tab of w
-        close t
       end repeat
     end repeat
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3c69b31..3b12e0c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -41408,6 +41408,7 @@
   <int value="4357" label="WebAuthnConditionalUiGet"/>
   <int value="4358" label="WebAuthnConditionalUiGetSuccess"/>
   <int value="4359" label="WebAuthnRkRequiredCreationSuccess"/>
+  <int value="4360" label="kDestructiveDocumentWriteAfterModuleScript"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -42130,6 +42131,9 @@
 </enum>
 
 <enum name="FetchFontName">
+  <obsolete>
+    Removed 09/2022. No longer needed.
+  </obsolete>
   <int value="0" label="Other"/>
   <int value="1" label="Google Sans Regular"/>
   <int value="2" label="Google Sans Medium"/>
@@ -42138,6 +42142,9 @@
 </enum>
 
 <enum name="FetchFontResult">
+  <obsolete>
+    Removed 09/2022. No longer needed.
+  </obsolete>
   <int value="0" label="Success"/>
   <int value="1" label="Failed due to unexpected font name"/>
   <int value="2" label="Failed with non-OK status code on result"/>
@@ -43213,6 +43220,13 @@
   <int value="7" label="Plugin VM App"/>
 </enum>
 
+<enum name="FileManagerTrashDirectorySetupFailedStep">
+  <int value="0" label="Failed creating info folder"/>
+  <int value="1" label="Failed creating files folder"/>
+  <int value="2" label="Failed setting the extended attributes"/>
+  <int value="3" label="Failed setting parent folder permissions"/>
+</enum>
+
 <enum name="FileManagerVolumeType">
 <!-- Keep this in sync with the JS value: QuickViewUma.VolumeType. -->
 
@@ -58898,6 +58912,7 @@
   <int value="-617851128"
       label="ForceGpuMainThreadToNormalPriorityDrDc:enabled"/>
   <int value="-617452890" label="media-router"/>
+  <int value="-617378214" label="private-aggregation-debug-mode"/>
   <int value="-616818899" label="SameSiteByDefaultCookies:enabled"/>
   <int value="-616508634" label="DisableCryptAuthV1DeviceSync:enabled"/>
   <int value="-615974325" label="ArcMouseWheelSmoothScroll:disabled"/>
@@ -82590,6 +82605,26 @@
   <int value="11" label="VersionButton"/>
 </enum>
 
+<enum name="QsFeatureCatalogName">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Network"/>
+  <int value="2" label="Bluetooth"/>
+  <int value="3" label="Accessibility"/>
+  <int value="4" label="QuietMode"/>
+  <int value="5" label="RotationLock"/>
+  <int value="6" label="PrivacyScreen"/>
+  <int value="7" label="CaptureMode"/>
+  <int value="8" label="NearbyShare"/>
+  <int value="9" label="NightLight"/>
+  <int value="10" label="Cast"/>
+  <int value="11" label="VPN"/>
+  <int value="12" label="IME"/>
+  <int value="13" label="Locale"/>
+  <int value="14" label="DarkMode"/>
+  <int value="15" label="ShelfParty"/>
+  <int value="16" label="AutoZoom"/>
+</enum>
+
 <enum name="QueryTilesGroupPruneReason">
   <int value="0" label="Expired"/>
   <int value="1" label="InvalidLocale"/>
@@ -88026,7 +88061,7 @@
 </enum>
 
 <enum name="SegmentationSelectionFailureReason">
-  <int value="0" label="Segmentation platform is disabled"/>
+  <int value="0" label="DEPRECATED: Segmentation platform is disabled"/>
   <int value="1" label="Segment selection available in prefs"/>
   <int value="2" label="At least one segment is not ready"/>
   <int value="3"
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 4d26df7..71eb492b 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1703,7 +1703,10 @@
 </histogram>
 
 <histogram name="Android.FontLookup.FetchFontName" enum="FetchFontName"
-    expires_after="2022-10-30">
+    expires_after="2022-09-20">
+  <obsolete>
+    Removed 09/2022. No longer needed.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -1714,7 +1717,10 @@
 </histogram>
 
 <histogram name="Android.FontLookup.FetchFontResult" enum="FetchFontResult"
-    expires_after="2023-01-01">
+    expires_after="2022-09-20">
+  <obsolete>
+    Removed 09/2022. No longer needed.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -1726,7 +1732,7 @@
 </histogram>
 
 <histogram name="Android.FontLookup.GmsFontRequest.Time" units="ms"
-    expires_after="2023-01-01">
+    expires_after="2023-03-19">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 89090b3..74ad7e7 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -3597,6 +3597,31 @@
   <token key="TabletOrClamshell" variants="DisplayModes"/>
 </histogram>
 
+<histogram name="Ash.QuickSettings.Button.Activated" enum="QsButtonCatalogName"
+    expires_after="2023-09-08">
+  <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Recorded when any button in the quick settings view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Ash.QuickSettings.FeaturePod.{FeaturePodBehavior}"
+    enum="QsFeatureCatalogName" expires_after="2023-09-14">
+  <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Recorded when an action is done on any FeaturePod in the quick settings
+    view. {FeaturePodBehavior} can be toggled to enable the feature, toggle to
+    disable this feature, or go to the feature's detailed page.
+  </summary>
+  <token key="FeaturePodBehavior">
+    <variant name="DiveIn" summary="Go to the feature detailed page"/>
+    <variant name="ToggleOff" summary="Toggle to disable the feature"/>
+    <variant name="ToggleOn" summary="Toggle to enable the feature"/>
+  </token>
+</histogram>
+
 <histogram name="Ash.QuickSettings.UserJourneyTime" units="ms"
     expires_after="2023-04-01">
   <owner>kradtke@chromium.org</owner>
@@ -3608,18 +3633,23 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.QuickSettings.{QsChildView}.Activated"
-    enum="QsButtonCatalogName" expires_after="2023-09-08">
+<histogram name="Ash.QuickSettings.{DeviceMode}.FeaturePodCountOnOpen"
+    units="count" expires_after="2023-09-14">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
-    Recorded when any {QsChildView} in the quick settings view is activated. For
-    example, this view can be a button (such as lock button), or a pod view
-    (such as the network pod).
+    The number of feature pods (e.g. WiFi, Bluetooth, IME, Accessibility, etc.)
+    in the system tray quick settings when it is opened, including pods that
+    overflow to other pages. This metric is logged by 2 different types of
+    {DeviceMode}: &quot;Clamshell&quot; and &quot;Tablet&quot;. This metric gets
+    recorded for both the new quick settings view and the old unified system
+    view, see
+    &quot;Ash.UnifiedSystemView.{DeviceMode}.FeaturePodCountOnOpen&quot; which
+    is the metric for the old view.
   </summary>
-  <token key="QsChildView">
-    <variant name="Button" summary="Buttons"/>
-    <variant name="FeaturePod" summary="Feature pods"/>
+  <token key="DeviceMode">
+    <variant name="Clamshell" summary="Regular mode"/>
+    <variant name="Tablet" summary="Tablet mode"/>
   </token>
 </histogram>
 
@@ -4514,18 +4544,47 @@
   </summary>
 </histogram>
 
-<histogram name="Ash.UnifiedSystemView.{OldQsChildView}.Activated"
-    enum="QsButtonCatalogName" expires_after="2023-09-08">
+<histogram name="Ash.UnifiedSystemView.Button.Activated"
+    enum="QsButtonCatalogName" expires_after="2023-09-14">
   <owner>jiamingc@google.com</owner>
   <owner>cros-status-area-eng@google.com</owner>
   <summary>
-    Recorded when any {OldQsChildView} in the old quick settings view is
-    activated. For example, this view can be a button (such as lock button), or
-    a pod view (such as the network pod).
+    Recorded when any button in the old quick settings view is activated.
   </summary>
-  <token key="OldQsChildView">
-    <variant name="Button" summary="Buttons"/>
-    <variant name="FeaturePod" summary="Feature pods"/>
+</histogram>
+
+<histogram name="Ash.UnifiedSystemView.FeaturePod.{FeaturePodBehavior}"
+    enum="QsFeatureCatalogName" expires_after="2023-09-14">
+  <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    Recorded when an action is done on any FeaturePod in the old quick settings
+    view. {FeaturePodBehavior} can be toggled to enable the feature, toggle to
+    disable this feature, or go to the feature's detailed page.
+  </summary>
+  <token key="FeaturePodBehavior">
+    <variant name="DiveIn" summary="Go to the feature detailed page"/>
+    <variant name="ToggleOff" summary="Toggle to disable the feature"/>
+    <variant name="ToggleOn" summary="Toggle to enable the feature"/>
+  </token>
+</histogram>
+
+<histogram name="Ash.UnifiedSystemView.{DeviceMode}.FeaturePodCountOnOpen"
+    units="count" expires_after="2023-09-14">
+  <owner>jiamingc@google.com</owner>
+  <owner>cros-status-area-eng@google.com</owner>
+  <summary>
+    The number of feature pods (e.g. WiFi, Bluetooth, IME, Accessibility, etc.)
+    in the old system tray quick settings when it is opened, including pods that
+    overflow to other pages. This metric is logged by 2 different types of
+    {DeviceMode}: &quot;Clamshell&quot; and &quot;Tablet&quot;. This metric gets
+    recorded for both the new quick settings view and the old unified system
+    view, see &quot;Ash.QuickSettings.{DeviceMode}.FeaturePodCountOnOpen&quot;
+    which is the metric for the new quick settings view.
+  </summary>
+  <token key="DeviceMode">
+    <variant name="Clamshell" summary="Regular mode"/>
+    <variant name="Tablet" summary="Tablet mode"/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index ee6320d..5bd4b6e 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1854,19 +1854,6 @@
   </summary>
 </histogram>
 
-<histogram name="ChromeOS.SystemTray.FeaturePodCountOnOpen" units="count"
-    expires_after="2023-04-01">
-  <owner>amehfooz@chromium.org</owner>
-  <owner>cros-status-area@google.com</owner>
-  <summary>
-    The number of feature pods (e.g. WiFi, Bluetooth, IME, Accessibility, etc.)
-    in the system tray quick settings when it is opened, including pods that
-    overflow to other pages. See
-    ChromeOS.SystemTray.Tablet.FeaturePodCountOnOpen for the corresponding
-    histogram logged in tablet mode.
-  </summary>
-</histogram>
-
 <histogram name="ChromeOS.SystemTray.FirstInteraction"
     enum="CrosSystemTrayFirstInteraction" expires_after="2023-04-01">
   <owner>amehfooz@chromium.org</owner>
@@ -1974,18 +1961,6 @@
   </summary>
 </histogram>
 
-<histogram name="ChromeOS.SystemTray.Tablet.FeaturePodCountOnOpen"
-    units="count" expires_after="2023-05-10">
-  <owner>amehfooz@chromium.org</owner>
-  <owner>cros-status-area@google.com</owner>
-  <summary>
-    The number of feature pods (e.g. WiFi, Bluetooth, IME, Accessibility, etc.)
-    in the system tray quick settings when it is opened, including pods that
-    overflow to other pages. This histogram is only logged when the device is in
-    tablet mode, otherwise ChromeOS.SystemTray.FeaturePodCountOnOpen is logged.
-  </summary>
-</histogram>
-
 <histogram name="ChromeOS.SystemTray.Tablet.ShelfPodCount" units="count"
     expires_after="2023-04-01">
   <owner>amehfooz@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 5d1720e..f06b40420 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -776,6 +776,16 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.Trash.DirectorySetupFailed"
+    enum="FileManagerTrashDirectorySetupFailedStep" expires_after="2023-03-20">
+  <owner>simmonsjosh@google.com</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>
+    Recorded when failing to create the trash folder (e.g. .Trash) and it's
+    children (e.g. info and files).
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.TrashFiles.{RootType}" units="units"
     expires_after="2023-02-28">
   <owner>simmonsjosh@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index b10d071..1df4eb9 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -1466,6 +1466,17 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.SupportsDisableMsaa" enum="BooleanSupported"
+    expires_after="2023-03-19">
+  <owner>vasilyt@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    This metric records if we had support for non-aa draws with hardware MSAA
+    via GL_EXT_multisample_compatibility. Recorded once for each raster task
+    that had non-aa draw.
+  </summary>
+</histogram>
+
 <histogram name="GPU.SupportsDX12" enum="BooleanSupported"
     expires_after="2023-02-19">
   <owner>magchen@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 9225748..bafc2ee 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -11898,6 +11898,9 @@
 
 <histogram name="SBMultipartUploader.FailedUploadDuration" units="s"
     expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -11912,6 +11915,9 @@
 
 <histogram name="SBMultipartUploader.NetworkRequestResponseCodeOrError"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -11927,6 +11933,9 @@
 
 <histogram name="SBMultipartUploader.RetriesNeeded" units="retries"
     expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -11940,6 +11949,9 @@
 
 <histogram name="SBMultipartUploader.SuccessfulUploadDuration" units="s"
     expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -11953,6 +11965,9 @@
 
 <histogram name="SBMultipartUploader.UploadSize" units="bytes"
     expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -11967,6 +11982,9 @@
 
 <histogram name="SBMultipartUploader.UploadSuccess" enum="BooleanSuccess"
     expires_after="2022-09-29">
+  <obsolete>
+    Removed 09-2022 due to lack of use and historical stability.
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 491334a..73a598c 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -141,7 +141,7 @@
 </histogram>
 
 <histogram name="Quota.GlobalUsageOfTemporaryStorage" units="MB"
-    expires_after="2022-10-23">
+    expires_after="2023-10-23">
   <owner>ayui@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>Global usage of temporary storage.</summary>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index d5a303e5..814f301 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -1384,7 +1384,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.FetcherNetErrorCode" enum="NetErrorCodes"
-    expires_after="2022-10-09">
+    expires_after="2023-08-31">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@google.com</owner>
   <summary>Records the net error code get from TileFetcher.</summary>
@@ -1442,7 +1442,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.ShowQueryTilesSegmentationResult"
-    enum="ShowQueryTilesSegmentationResult" expires_after="2022-08-09">
+    enum="ShowQueryTilesSegmentationResult" expires_after="2023-08-31">
   <owner>haileywang@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -1469,7 +1469,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.Tile.Clicked" units="index"
-    expires_after="2022-10-31">
+    expires_after="2023-08-31">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1490,7 +1490,7 @@
 </histogram>
 
 <histogram base="true" name="Search.QueryTiles.TileCount" units="tiles"
-    expires_after="2022-10-31">
+    expires_after="2023-08-31">
 <!-- Name completed by histogram_suffixes name="TileUiSurface" -->
 
   <owner>shaktisahu@chromium.org</owner>
@@ -1520,7 +1520,7 @@
 </histogram>
 
 <histogram name="Search.QueryTiles.TrendingTileEvent" enum="TrendingTileEvent"
-    expires_after="2023-03-05">
+    expires_after="2023-08-31">
   <owner>qinmin@chromium.org</owner>
   <owner>chrome-upboarding-eng@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 b1f89d2..8fd020fe 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": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "464349636569a9f96e9ee2dc440a85667ef862db",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3a9f96aecc5ffe0b5a9d6d540653468f0b215842/trace_processor_shell.exe"
+            "hash": "5877059958a25c74b31d61fc9a54026db4e081c1",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/b2829a1f2f115581d5ab091770e7c1878c243b82/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "120c76863562e2e62a7c3c94906fb617d1b04bea",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3a9f96aecc5ffe0b5a9d6d540653468f0b215842/trace_processor_shell"
+            "hash": "03f2a7d05c23c72113d5e02088863f980a573f1c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/40b17af5c58d2777097eef8fb8a7d61c8419af7b/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc
index 40b25383..03cb2d4e 100644
--- a/ui/base/l10n/l10n_util.cc
+++ b/ui/base/l10n/l10n_util.cc
@@ -58,6 +58,7 @@
 
 static const char* const kAcceptLanguageList[] = {
     "af",  // Afrikaans
+    "ak",  // Twi
     "am",  // Amharic
     "an",  // Aragonese
     "ar",  // Arabic
@@ -85,6 +86,7 @@
     "de-CH",           // German (Switzerland)
     "de-DE",           // German (Germany)
     "de-LI",           // German (Liechtenstein)
+    "ee",              // Ewe
     "el",              // Greek
     "en",              // English
     "en-AU",           // English (Australia)
@@ -152,10 +154,12 @@
     "kn",      // Kannada
     "ko",      // Korean
     "kok",     // Konkani
+    "kri",     // Krio
     "ku",      // Kurdish
     "ky",      // Kyrgyz
     "la",      // Latin
     "lb",      // Luxembourgish
+    "lg",      // Luganda
     "ln",      // Lingala
     "lo",      // Laothian
     "lt",      // Lithuanian
@@ -175,6 +179,7 @@
     "nl",      // Dutch
     "nn",      // Norwegian (Nynorsk)
     "no",      // Norwegian
+    "nso",     // Sepedi
     "ny",      // Nyanja
     "oc",      // Occitan
     "om",      // Oromo
diff --git a/ui/webui/resources/js/cr/ui/context_menu_handler.d.ts b/ui/webui/resources/js/cr/ui/context_menu_handler.d.ts
deleted file mode 100644
index 451c6d2..0000000
--- a/ui/webui/resources/js/cr/ui/context_menu_handler.d.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {Menu} from './menu.js';
-import {HideType} from './menu_button.js';
-
-export const contextMenuHandler: ContextMenuHandler;
-
-declare class ContextMenuHandler extends EventTarget implements
-    EventListenerObject {
-  get menu(): Menu;
-  showMenu(e: Event, menu: Menu): void;
-  hideMenu(hideType?: HideType|undefined): void;
-  handleEvent(e: Event): void;
-  addContextMenuProperty(elementOrClass: Element|Function): void;
-  setContextMenu(element: Element, contextMenu: Menu): void;
-}
diff --git a/ui/webui/resources/js/cr/ui/menu_button.d.ts b/ui/webui/resources/js/cr/ui/menu_button.d.ts
deleted file mode 100644
index 8832a17..0000000
--- a/ui/webui/resources/js/cr/ui/menu_button.d.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export type HideType = number;
-
-export namespace HideType {
-  export const INSTANT: number;
-  export const DELAYED: number;
-}
-
-export class MenuButton extends HTMLButtonElement implements
-    EventListenerObject {
-  handleEvent(e: Event): void;
-}
diff --git a/ui/webui/resources/js/cr/ui/menu_item.d.ts b/ui/webui/resources/js/cr/ui/menu_item.d.ts
deleted file mode 100644
index fcec10fd..0000000
--- a/ui/webui/resources/js/cr/ui/menu_item.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export class MenuItem extends HTMLElement {
-  disabled: boolean;
-  hidden: any;
-  selected: boolean;
-  checked: boolean;
-  checkable: boolean;
-  handleEvent(e: Event): void;
-}