diff --git a/DEPS b/DEPS
index 076f2ab..c55c132 100644
--- a/DEPS
+++ b/DEPS
@@ -280,11 +280,11 @@
   # 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': 'bdd0205ae470610e70420e545fe952cdbae7ab48',
+  'skia_revision': '9e3ab88198ac013e38d177b78f6ab138913d4ef0',
   # 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': '858f681e0d0324372ade44a4b88254632f1b6f7b',
+  'v8_revision': '2bb9b03df2098a3319e37b1d5d96101267b4948a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -307,7 +307,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220623.3.1',
+  'fuchsia_version': 'version:8.20220624.0.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -351,7 +351,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': '3236751e83850cf192b0d6017a0ba34542653592',
+  'catapult_revision': 'f6dcfa4c72d86b430ae4f6f3b6ccfe53c6ced728',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -359,7 +359,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': '69ed2407734d98fdbaaabaac91fb679ca73bd88c',
+  'devtools_frontend_revision': 'cc1e8af47b7b1a6a36f0dcf035f11bc23b61f8b7',
   # 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.
@@ -395,7 +395,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': 'f99671b830811d33dc1d1e234c4b66300ed007a7',
+  'dawn_revision': '6241cf9632b118060ea4e377cf5798d0a5a17806',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -929,7 +929,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '4P3G5yy2a9JRgJERG8h3ApAoreMO-OjDG4RMhEcnZDYC',
+          'version': 'Oj-D7aKfxG6EL-AgXAVR4klwdS-wUElBPLy0LY9ljLMC',
       },
     ],
     'condition': 'checkout_android',
@@ -1553,7 +1553,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '2d2d50afec5365cdb39f1e9af40ab8fdde5387d8',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '4ce89ccad099bc5371249acc64346d502bf8acde',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1720,10 +1720,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'e58ed2132aa47ac110a4cce1763abfa34f4fa34e',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'cef4c767813032183d4554d3ba2dc6f1999988b9',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '614d1a8eab3d5ec6cb0a77bdb6d4be886c72ce11',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '5dc460fca5987fd33bb5d8b145e65a8d8e43974f',
+    Var('webrtc_git') + '/src.git' + '@' + '2fdf222da3691466c81214c2dd90d92b9ec922fe',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1796,7 +1796,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@52f7a151aba85a8ec25e7146c7ce4de090da6a34',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7ce67899e31dbfea10fa05670a3be09e000fe7d4',
     'condition': 'checkout_src_internal',
   },
 
@@ -1848,7 +1848,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'XRmvsftnXP2wAcO0GGVbuiQ5QOT7wGrRvzoAcUmBZlAC',
+        'version': 'SUOawKs7D8d9s81tTqcq1ihr9fA_5aL16Vn61aYAEm4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 02af42b..0918e663 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -892,9 +892,9 @@
     }
   }
 
-  const WindowSnapWMEvent event(action == WINDOW_CYCLE_SNAP_LEFT
-                                    ? WM_EVENT_CYCLE_SNAP_PRIMARY
-                                    : WM_EVENT_CYCLE_SNAP_SECONDARY);
+  const WMEvent event(action == WINDOW_CYCLE_SNAP_LEFT
+                          ? WM_EVENT_CYCLE_SNAP_PRIMARY
+                          : WM_EVENT_CYCLE_SNAP_SECONDARY);
   aura::Window* active_window = window_util::GetActiveWindow();
   DCHECK(active_window);
 
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 371da3b..84ba4d5 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -340,7 +340,7 @@
 
   // Pins |item2| by dragging it to ShelfView.
   ShelfView* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting();
-  ASSERT_EQ(0u, shelf_view->view_model()->view_size());
+  ASSERT_EQ(0, shelf_view->view_model()->view_size());
   GetEventGenerator()->MoveMouseTo(item2->GetBoundsInScreen().CenterPoint());
   GetEventGenerator()->PressLeftButton();
   item2->FireMouseDragTimerForTest();
@@ -348,7 +348,7 @@
       shelf_view->GetBoundsInScreen().CenterPoint());
   ASSERT_TRUE(GetAppsGridView()->FireDragToShelfTimerForTest());
   GetEventGenerator()->ReleaseLeftButton();
-  ASSERT_EQ(1u, shelf_view->view_model()->view_size());
+  ASSERT_EQ(1, shelf_view->view_model()->view_size());
 
   // Verifies that the dragged item has the correct previous/next focusable
   // view after drag.
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 5e0d49b9..c65b9cf 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -888,7 +888,7 @@
   EXPECT_EQ(original_folder_item_bounds, folder_item_view->GetBoundsInScreen());
 
   // No item layers are expected to be created.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1003,7 +1003,7 @@
             apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1122,7 +1122,7 @@
             apps_grid_view_->GetItemViewAt(2)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1241,7 +1241,7 @@
             apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1324,7 +1324,7 @@
             apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1422,7 +1422,7 @@
             apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -1519,7 +1519,7 @@
             apps_grid_view_->GetItemViewAt(3)->GetBoundsInScreen());
 
   // Verify that item view layers have been deleted.
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -2969,7 +2969,7 @@
   // Verify that item layers have been destroyed after the drag operation ended.
   apps_grid_test_api_->WaitForItemMoveAnimationDone();
 
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -2984,7 +2984,7 @@
   EXPECT_EQ(expected_folder_item_view_bounds,
             folder_item_view->GetBoundsInScreen());
   EXPECT_TRUE(AppListIsInFolderView());
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -3043,7 +3043,7 @@
   // Verify that item layers have been destroyed after the drag operation ended.
   apps_grid_test_api_->WaitForItemMoveAnimationDone();
 
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -3065,7 +3065,7 @@
   // Verify that item views have no layers after the folder has been opened.
   apps_grid_test_api_->WaitForItemMoveAnimationDone();
   EXPECT_TRUE(AppListIsInFolderView());
-  for (size_t i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view_->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view_->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
@@ -5965,7 +5965,7 @@
   // of the layers, including items on the second page.
   generator->MoveMouseTo(start_point.x(), 0);
   generator->ReleaseLeftButton();
-  for (size_t i = 0; i < apps_grid_view->view_model()->view_size(); ++i) {
+  for (int i = 0; i < apps_grid_view->view_model()->view_size(); ++i) {
     views::View* item_view = apps_grid_view->view_model()->view_at(i);
     EXPECT_FALSE(item_view->layer()) << "at " << i;
   }
diff --git a/ash/app_list/app_list_test_api.cc b/ash/app_list/app_list_test_api.cc
index d67e0faa..4bcefff 100644
--- a/ash/app_list/app_list_test_api.cc
+++ b/ash/app_list/app_list_test_api.cc
@@ -97,7 +97,7 @@
 
 ash::AppListItemView* FindFolderItemView(ash::AppsGridView* apps_grid_view) {
   auto* model = apps_grid_view->view_model();
-  for (size_t index = 0; index < model->view_size(); ++index) {
+  for (int index = 0; index < model->view_size(); ++index) {
     ash::AppListItemView* current_view = model->view_at(index);
     if (current_view->is_folder())
       return current_view;
@@ -108,7 +108,7 @@
 
 ash::AppListItemView* FindNonFolderItemView(ash::AppsGridView* apps_grid_view) {
   auto* model = apps_grid_view->view_model();
-  for (size_t index = 0; index < model->view_size(); ++index) {
+  for (int index = 0; index < model->view_size(); ++index) {
     ash::AppListItemView* current_view = model->view_at(index);
     if (!current_view->is_folder())
       return current_view;
@@ -158,7 +158,7 @@
     ui::test::EventGenerator* event_generator) {
   views::MenuItemView* root_menu = nullptr;
 
-  EXPECT_GT(apps_grid_view->view_model()->view_size(), 0u);
+  EXPECT_GT(apps_grid_view->view_model()->view_size(), 0);
 
   switch (menu_type) {
     case AppListTestApi::MenuType::kAppListPageMenu:
@@ -415,7 +415,7 @@
     const std::string& item_id) {
   views::ViewModelT<AppListItemView>* view_model =
       GetTopLevelAppsGridView()->view_model();
-  for (size_t i = 0; i < view_model->view_size(); ++i) {
+  for (int i = 0; i < view_model->view_size(); ++i) {
     AppListItemView* app_list_item_view = view_model->view_at(i);
     if (app_list_item_view->item()->id() == item_id)
       return app_list_item_view->title()->GetText();
@@ -426,7 +426,7 @@
 std::vector<std::string> AppListTestApi::GetTopLevelViewIdList() {
   std::vector<std::string> id_list;
   auto* view_model = GetTopLevelAppsGridView()->view_model();
-  for (size_t i = 0; i < view_model->view_size(); ++i) {
+  for (int i = 0; i < view_model->view_size(); ++i) {
     AppListItem* app_list_item = view_model->view_at(i)->item();
     if (app_list_item) {
       id_list.push_back(app_list_item->id());
@@ -633,8 +633,7 @@
 void AppListTestApi::VerifyTopLevelItemVisibility() {
   auto* view_model = GetTopLevelAppsGridView()->view_model();
   std::vector<std::string> invisible_item_names;
-  for (size_t view_index = 0; view_index < view_model->view_size();
-       ++view_index) {
+  for (int view_index = 0; view_index < view_model->view_size(); ++view_index) {
     auto* item_view = view_model->view_at(view_index);
     if (!item_view->GetVisible())
       invisible_item_names.push_back(item_view->item()->name());
diff --git a/ash/app_list/paged_view_structure.cc b/ash/app_list/paged_view_structure.cc
index 38864e5..29441dd 100644
--- a/ash/app_list/paged_view_structure.cc
+++ b/ash/app_list/paged_view_structure.cc
@@ -67,7 +67,7 @@
   if (mode_ == Mode::kSinglePage) {
     // Copy the view model to a single page.
     pages_[0].reserve(view_model->view_size());
-    for (size_t i = 0; i < view_model->view_size(); ++i) {
+    for (int i = 0; i < view_model->view_size(); ++i) {
       pages_[0].push_back(view_model->view_at(i));
     }
     return;
@@ -75,7 +75,7 @@
 
   if (mode_ == Mode::kFullPages) {
     // Copy the view model to N full pages.
-    for (size_t i = 0; i < view_model->view_size(); ++i) {
+    for (int i = 0; i < view_model->view_size(); ++i) {
       if (pages_.back().size() ==
           static_cast<size_t>(TilesPerPage(pages_.size() - 1))) {
         pages_.emplace_back();
@@ -264,7 +264,7 @@
     return view_model->view_size();
 
   AppListItemView* view = pages_[index.page][index.slot];
-  return view_model->GetIndexOfView(view).value();
+  return view_model->GetIndexOfView(view);
 }
 
 GridIndex PagedViewStructure::GetLastTargetIndex() const {
@@ -272,7 +272,7 @@
     return GridIndex(0, 0);
 
   if (mode_ == Mode::kSinglePage || mode_ == Mode::kFullPages) {
-    size_t view_index = apps_grid_view_->view_model()->view_size();
+    int view_index = apps_grid_view_->view_model()->view_size();
 
     // If a view in the current view model is being dragged, then ignore it.
     if (apps_grid_view_->drag_view())
@@ -304,7 +304,7 @@
   }
 
   const int page_size = total_pages();
-  DCHECK_LT(0u, apps_grid_view_->view_model()->view_size());
+  DCHECK_LT(0, apps_grid_view_->view_model()->view_size());
   DCHECK_LE(page_index, page_size);
 
   if (page_index == page_size)
diff --git a/ash/app_list/views/app_list_bubble_view_unittest.cc b/ash/app_list/views/app_list_bubble_view_unittest.cc
index dbc27b26..845568f 100644
--- a/ash/app_list/views/app_list_bubble_view_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_view_unittest.cc
@@ -989,7 +989,7 @@
 
   // There are only 2 folders, and hence 2 columns, in the top level apps grid.
   auto* apps_grid_view = GetAppsGridView();
-  ASSERT_EQ(2u, apps_grid_view->view_model()->view_size());
+  ASSERT_EQ(2, apps_grid_view->view_model()->view_size());
 
   // Focus the 5th recent app.
   auto* recent_apps_view = GetRecentAppsView();
diff --git a/ash/app_list/views/app_list_main_view_unittest.cc b/ash/app_list/views/app_list_main_view_unittest.cc
index f1bd064..7adb0cffc 100644
--- a/ash/app_list/views/app_list_main_view_unittest.cc
+++ b/ash/app_list/views/app_list_main_view_unittest.cc
@@ -38,7 +38,7 @@
 namespace ash {
 namespace {
 
-const size_t kInitialItems = 2;
+const int kInitialItems = 2;
 
 }  // namespace
 
@@ -184,7 +184,7 @@
               delegate_->GetTestModel()->FindFolderItem("single_item_folder"));
     EXPECT_EQ(AppListFolderItem::kItemType, folder_item->GetItemType());
 
-    EXPECT_EQ(1u, GetRootViewModel()->view_size());
+    EXPECT_EQ(1, GetRootViewModel()->view_size());
     AppListItemView* folder_item_view =
         static_cast<AppListItemView*>(GetRootViewModel()->view_at(0));
     EXPECT_EQ(folder_item_view->item(), folder_item);
@@ -287,7 +287,7 @@
   std::unique_ptr<SearchModel> old_search_model(
       delegate_->ReleaseTestSearchModel());
 
-  const size_t kReplacementItems = 5;
+  const int kReplacementItems = 5;
   delegate_->ReplaceTestModel(kReplacementItems);
   EXPECT_EQ(kReplacementItems, GetRootViewModel()->view_size());
 }
@@ -300,11 +300,11 @@
 
   // Number of apps to populate. Should provide more than 1 page of apps (5*4 =
   // 20).
-  const size_t kNumApps = 30;
+  const int kNumApps = 30;
   delegate_->GetTestModel()->PopulateApps(kNumApps);
   GetRootGridView()->Layout();
 
-  EXPECT_EQ(1u, GetFolderViewModel()->view_size());
+  EXPECT_EQ(1, GetFolderViewModel()->view_size());
   EXPECT_EQ(kNumApps + 1, GetRootViewModel()->view_size());
 
   AppListItemView* dragged = StartDragForReparent(0);
@@ -372,7 +372,7 @@
   GetFolderGridView()->EndDrag(false);
 
   // The app list model should remain unchanged.
-  EXPECT_EQ(2u, GetRootViewModel()->view_size());
+  EXPECT_EQ(2, GetRootViewModel()->view_size());
   EXPECT_EQ(folder_id, GetRootGridView()->GetItemViewAt(0)->item()->id());
   AppListFolderItem* const folder_item =
       delegate_->GetTestModel()->FindFolderItem("single_item_folder");
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index 6977cd3c..d26880ab 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -1454,7 +1454,7 @@
   forward_view_list.push_back(suggestions[0]);
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view()->view_model();
-  for (size_t i = 0; i < view_model->view_size(); i += apps_grid_view()->cols())
+  for (int i = 0; i < view_model->view_size(); i += apps_grid_view()->cols())
     forward_view_list.push_back(view_model->view_at(i));
   forward_view_list.push_back(search_box_view()->search_box());
 
@@ -1463,7 +1463,7 @@
 
   std::vector<views::View*> backward_view_list;
   backward_view_list.push_back(search_box_view()->search_box());
-  for (size_t i = view_model->view_size() - 1; i < view_model->view_size();
+  for (int i = view_model->view_size() - 1; i >= 0;
        i -= apps_grid_view()->cols())
     backward_view_list.push_back(view_model->view_at(i));
   // Up key will always move focus to the last suggestion chip from first row
@@ -1531,7 +1531,7 @@
   std::vector<views::View*> forward_view_list;
   const views::ViewModelT<AppListItemView>* view_model =
       app_list_folder_view()->items_grid_view()->view_model();
-  for (size_t i = 0; i < view_model->view_size();
+  for (int i = 0; i < view_model->view_size();
        i += app_list_folder_view()->items_grid_view()->cols()) {
     forward_view_list.push_back(view_model->view_at(i));
   }
@@ -1548,7 +1548,7 @@
   backward_view_list.push_back(search_box_view()->search_box());
   backward_view_list.push_back(
       app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
-  for (size_t i = view_model->view_size() - 1; i < view_model->view_size();
+  for (int i = view_model->view_size() - 1; i >= 0;
        i -= app_list_folder_view()->items_grid_view()->cols()) {
     backward_view_list.push_back(view_model->view_at(i));
   }
@@ -1579,7 +1579,7 @@
   std::vector<views::View*> forward_view_list;
   const views::ViewModelT<AppListItemView>* view_model =
       app_list_folder_view()->items_grid_view()->view_model();
-  for (size_t i = kMaxItemsPerFolderPage; i < view_model->view_size();
+  for (int i = kMaxItemsPerFolderPage; i < view_model->view_size();
        i += app_list_folder_view()->items_grid_view()->cols()) {
     forward_view_list.push_back(view_model->view_at(i));
   }
@@ -1596,7 +1596,7 @@
   backward_view_list.push_back(search_box_view()->search_box());
   backward_view_list.push_back(
       app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
-  for (size_t i = view_model->view_size() - 1; i < view_model->view_size();
+  for (int i = view_model->view_size() - 1; i >= 0;
        i -= app_list_folder_view()->items_grid_view()->cols()) {
     backward_view_list.push_back(view_model->view_at(i));
   }
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index caed8df..7851b1c 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -364,7 +364,7 @@
 
   // The app list item view icon sizes depend on the app list config, so they
   // have to be refreshed.
-  for (size_t i = 0; i < view_model_.view_size(); ++i)
+  for (int i = 0; i < view_model_.view_size(); ++i)
     view_model_.view_at(i)->UpdateAppListConfig(app_list_config);
 
   if (current_ghost_view_)
@@ -413,7 +413,7 @@
 
   // The number of non-page-break-items should be the same as item views.
   if (item_list_) {
-    size_t item_count = 0;
+    int item_count = 0;
     for (size_t i = 0; i < item_list_->item_count(); ++i) {
       if (!item_list_->item_at(i)->is_page_break())
         ++item_count;
@@ -758,7 +758,7 @@
   if (!cancel) {
     // Select the page where dragged item is dropped. Avoid doing so when the
     // dragged item ends up in a folder.
-    const size_t model_index = GetModelIndexOfItem(drag_item);
+    const int model_index = GetModelIndexOfItem(drag_item);
     if (model_index < view_model_.view_size())
       EnsureViewVisible(view_structure_.GetIndexFromModelIndex(model_index));
   }
@@ -781,7 +781,7 @@
 }
 
 AppListItemView* AppsGridView::GetItemViewAt(int index) const {
-  if (index < 0 || static_cast<size_t>(index) >= view_model_.view_size())
+  if (index < 0 || index >= view_model_.view_size())
     return nullptr;
   return view_model_.view_at(index);
 }
@@ -863,7 +863,7 @@
   // the folder location change should be animated).
   AppListItemView* item_view = nullptr;
   int model_index = -1;
-  for (size_t i = 0; i < view_model_.view_size(); ++i) {
+  for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = view_model_.view_at(i);
     if (view == drag_view_)
       continue;
@@ -1052,7 +1052,7 @@
     const views::ViewHierarchyChangedDetails& details) {
   if (!details.is_add && details.parent == items_container_) {
     // The view being delete should not have reference in |view_model_|.
-    CHECK(!view_model_.GetIndexOfView(details.child).has_value());
+    CHECK_EQ(-1, view_model_.GetIndexOfView(details.child));
 
     if (selected_view_ == details.child)
       selected_view_ = nullptr;
@@ -1142,15 +1142,14 @@
                                        AppListItemView* app_view) {
   delete placeholder;
 
-  if (view_model_.GetIndexOfView(app_view).has_value() &&
-      !ItemViewsRequireLayers())
+  if (view_model_.GetIndexOfView(app_view) != -1 && !ItemViewsRequireLayers())
     app_view->DestroyLayer();
 
   UpdatePulsingBlockViews();
 }
 
 AppListItemView* AppsGridView::MaybeSwapPlaceholderAsset(size_t index) {
-  size_t model_index = GetTargetModelIndexFromItemIndex(index);
+  int model_index = GetTargetModelIndexFromItemIndex(index);
   AppListItemView* view = items_container_->AddChildViewAt(
       CreateViewForItemAtIndex(index), model_index);
   view_model_.Add(view, model_index);
@@ -1201,9 +1200,9 @@
     if (existing_items > TilesPerPage(0))
       existing_items -= TilesPerPage(0);
   }
-  const size_t available_slots =
+  const int available_slots =
       tiles_per_page - (existing_items % tiles_per_page);
-  const size_t desired =
+  const int desired =
       model_ && model_->status() == AppListModelStatus::kStatusSyncing
           ? available_slots
           : 0;
@@ -1260,11 +1259,11 @@
 }
 
 GridIndex AppsGridView::GetIndexOfView(const AppListItemView* view) const {
-  const auto model_index = view_model_.GetIndexOfView(view);
-  if (!model_index.has_value())
+  const int model_index = view_model_.GetIndexOfView(view);
+  if (model_index == -1)
     return GridIndex();
 
-  return view_structure_.GetIndexFromModelIndex(model_index.value());
+  return view_structure_.GetIndexFromModelIndex(model_index);
 }
 
 AppListItemView* AppsGridView::GetViewAtIndex(const GridIndex& index) const {
@@ -1305,7 +1304,7 @@
 }
 
 void AppsGridView::SetIdealBoundsForViewToGridIndex(
-    size_t view_index_in_model,
+    int view_index_in_model,
     const GridIndex& view_grid_index) {
   gfx::Rect tile_bounds = GetExpectedTileBounds(view_grid_index);
   tile_bounds.Offset(CalculateTransitionOffset(view_grid_index.page));
@@ -1333,10 +1332,10 @@
     reserved_slots.insert(open_folder_info_->grid_index);
   }
 
-  const size_t total_views =
+  const int total_views =
       view_model_.view_size() + pulsing_blocks_model_.view_size();
   int slot_index = 0;
-  for (size_t i = 0; i < total_views; ++i) {
+  for (int i = 0; i < total_views; ++i) {
     // NOTE: Because of pulsing blocks, `i` can count up to a value higher than
     // the view model size. So verify that `i` is less than the view model size
     // before fetching at index `i` from the view model.
@@ -1418,7 +1417,7 @@
 
   // All pulsing blocks come after item views.
   GridIndex pulsing_block_index = copied_view_structure.GetLastTargetIndex();
-  for (size_t i = 0; i < pulsing_blocks_model().view_size(); ++i) {
+  for (int i = 0; i < pulsing_blocks_model().view_size(); ++i) {
     if (pulsing_block_index.slot == TilesPerPage(pulsing_block_index.page)) {
       ++pulsing_block_index.page;
       pulsing_block_index.slot = 0;
@@ -1437,7 +1436,7 @@
   visible_bounds.set_origin(visible_origin);
 
   CalculateIdealBounds();
-  for (size_t i = 0; i < view_model_.view_size(); ++i) {
+  for (int i = 0; i < view_model_.view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
     const gfx::Rect& target = view_model_.ideal_bounds(i);
     const gfx::Rect& current = view->bounds();
@@ -1570,7 +1569,7 @@
   if (target_folder_id.empty()) {
     // Find the view for drag item, and use its ideal bounds to calculate target
     // drop bounds.
-    for (size_t i = 0; i < view_model_.view_size(); ++i) {
+    for (int i = 0; i < view_model_.view_size(); ++i) {
       if (view_model_.view_at(i)->item() != drag_item)
         continue;
 
@@ -1769,8 +1768,8 @@
 gfx::Rect AppsGridView::GetTargetIconRectInFolder(
     AppListItem* drag_item,
     AppListItemView* folder_item_view) {
-  const gfx::Rect view_ideal_bounds = view_model_.ideal_bounds(
-      view_model_.GetIndexOfView(folder_item_view).value());
+  const gfx::Rect view_ideal_bounds =
+      view_model_.ideal_bounds(view_model_.GetIndexOfView(folder_item_view));
   const gfx::Rect icon_ideal_bounds =
       folder_item_view->GetIconBoundsForTargetViewBounds(
           app_list_config_, view_ideal_bounds,
@@ -1946,7 +1945,7 @@
     bool cancel_drag,
     std::unique_ptr<AppDragIconProxy> drag_icon_proxy) {
   DCHECK(!IsInFolder());
-  DCHECK(view_model_.GetIndexOfView(original_parent_item_view).has_value());
+  DCHECK_NE(-1, view_model_.GetIndexOfView(original_parent_item_view));
 
   // EndDrag was called before if |drag_view_| is nullptr.
   if (!drag_item_)
@@ -2040,7 +2039,7 @@
   DCHECK(key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT ||
          key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN);
   DCHECK(!folder_delegate_);
-  DCHECK(view_model_.GetIndexOfView(original_parent_item_view).has_value());
+  DCHECK_NE(-1, view_model_.GetIndexOfView(original_parent_item_view));
 
   // Set |original_parent_item_view| selected so |target_index| will be
   // computed relative to the open folder.
@@ -2191,8 +2190,8 @@
   bounds_animator_->Cancel();
   drag_icon_proxy_.reset();
 
-  const size_t total_views = view_model_.view_size();
-  for (size_t i = 0; i < total_views; ++i) {
+  const int total_views = view_model_.view_size();
+  for (int i = 0; i < total_views; ++i) {
     if (view_model_.view_at(i)->layer())
       view_model_.view_at(i)->layer()->CompleteAllAnimations();
   }
@@ -2457,10 +2456,10 @@
   GridIndex start_index(GetSelectedPage(), 0);
   if (!IsValidIndex(start_index))
     return;
-  size_t start = view_structure_.GetModelIndexFromIndex(start_index);
-  size_t end =
+  int start = view_structure_.GetModelIndexFromIndex(start_index);
+  int end =
       std::min(view_model_.view_size(), start + TilesPerPage(start_index.page));
-  for (size_t i = start; i < end; ++i)
+  for (int i = start; i < end; ++i)
     GetItemViewAt(i)->CancelContextMenu();
 }
 
@@ -2762,10 +2761,8 @@
     const ui::KeyboardCode backward =
         base::i18n::IsRTL() ? ui::VKEY_RIGHT : ui::VKEY_LEFT;
 
-    size_t target_model_index =
-        view_model_.GetIndexOfView(selected_view_).value();
-    if (target_model_index > 0 || key_code != backward)
-      target_model_index += (key_code == backward) ? -1 : 1;
+    const int target_model_index = view_model_.GetIndexOfView(selected_view_) +
+                                   ((key_code == backward) ? -1 : 1);
 
     // A forward move on the last item in |view_model_| should result in page
     // creation.
@@ -2782,7 +2779,8 @@
     }
 
     target_index = GetIndexOfView(
-        static_cast<const AppListItemView*>(GetItemViewAt(target_model_index)));
+        static_cast<const AppListItemView*>(GetItemViewAt(std::min(
+            std::max(0, target_model_index), view_model_.view_size() - 1))));
     if (view_structure_.mode() == PagedViewStructure::Mode::kPartialPages &&
         key_code == backward && target_index.page < source_index.page &&
         !view_structure_.IsFullPage(target_index.page)) {
@@ -2942,7 +2940,7 @@
 bool AppsGridView::IsValidIndex(const GridIndex& index) const {
   return index.page >= 0 && index.page < GetTotalPages() && index.slot >= 0 &&
          index.slot < TilesPerPage(index.page) &&
-         static_cast<size_t>(view_structure_.GetModelIndexFromIndex(index)) <
+         view_structure_.GetModelIndexFromIndex(index) <
              view_model_.view_size();
 }
 
@@ -2984,7 +2982,7 @@
     return TilesPerPage(page);
 
   // We are on the last page, so calculate the number of items on the page.
-  size_t item_count = view_model_.view_size();
+  int item_count = view_model_.view_size();
   int current_page = 0;
   while (current_page < GetTotalPages() - 1) {
     item_count -= TilesPerPage(current_page);
@@ -3107,7 +3105,7 @@
 }
 
 void AppsGridView::PrepareItemsForBoundsAnimation() {
-  for (size_t i = 0; i < view_model_.view_size(); ++i)
+  for (int i = 0; i < view_model_.view_size(); ++i)
     view_model_.view_at(i)->EnsureLayer();
 }
 
@@ -3167,7 +3165,7 @@
     // painting cost incurred by the bounds changes because of reorder. The
     // fade in animation should be responsible for reshowing the item views that
     // are within the visible view port after reorder.
-    for (size_t view_index = 0; view_index < view_model_.view_size();
+    for (int view_index = 0; view_index < view_model_.view_size();
          ++view_index) {
       view_model_.view_at(view_index)->SetVisible(false);
     }
@@ -3210,8 +3208,7 @@
     layer()->SetOpacity(1.f);
 
   // Ensure that all item views are visible after fade in animation completes.
-  for (size_t view_index = 0; view_index < view_model_.view_size();
-       ++view_index) {
+  for (int view_index = 0; view_index < view_model_.view_size(); ++view_index) {
     view_model_.view_at(view_index)->SetVisible(true);
   }
 
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index 16f6f6d6..a68e060 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -469,7 +469,7 @@
   // Sets the ideal bounds for view at index `view_inde_in_model` in
   // `view_model_`. The bounds are set to match the expected tile bounds at
   // `view_grid_index` in the apps grid.
-  void SetIdealBoundsForViewToGridIndex(size_t view_index_in_model,
+  void SetIdealBoundsForViewToGridIndex(int view_index_in_model,
                                         const GridIndex& view_grid_index);
 
   // Calculates the item views' bounds for both folder and non-folder.
diff --git a/ash/app_list/views/apps_grid_view_test_api.cc b/ash/app_list/views/apps_grid_view_test_api.cc
index fadb2b6..ffe23666 100644
--- a/ash/app_list/views/apps_grid_view_test_api.cc
+++ b/ash/app_list/views/apps_grid_view_test_api.cc
@@ -92,7 +92,7 @@
       ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE));
 }
 
-size_t AppsGridViewTestApi::TilesPerPage(int page) const {
+int AppsGridViewTestApi::TilesPerPage(int page) const {
   return view_->TilesPerPage(page);
 }
 
diff --git a/ash/app_list/views/apps_grid_view_test_api.h b/ash/app_list/views/apps_grid_view_test_api.h
index d4c0664..4e26b2f 100644
--- a/ash/app_list/views/apps_grid_view_test_api.h
+++ b/ash/app_list/views/apps_grid_view_test_api.h
@@ -48,7 +48,7 @@
 
   void PressItemAt(int index);
 
-  size_t TilesPerPage(int page) const;
+  int TilesPerPage(int page) const;
 
   int AppsOnPage(int page) const;
 
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 2a19bd4..a3fbce6 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -423,9 +423,7 @@
     return test_api_->GetItemTileRectOnCurrentPageAt(row, col);
   }
 
-  size_t GetTilesPerPage(int page) const {
-    return test_api_->TilesPerPage(page);
-  }
+  int GetTilesPerPage(int page) const { return test_api_->TilesPerPage(page); }
 
   PaginationModel* GetPaginationModel() const {
     DCHECK(paged_apps_grid_view_) << "Only available in tablet mode or when "
@@ -507,11 +505,11 @@
   void TestAppListItemViewIndice() {
     const views::ViewModelT<AppListItemView>* view_model =
         apps_grid_view_->view_model();
-    DCHECK_GT(view_model->view_size(), 0u);
+    DCHECK_GT(view_model->view_size(), 0);
     views::View* items_container = apps_grid_view_->items_container_;
     auto app_iter = items_container->FindChild(view_model->view_at(0));
     DCHECK(app_iter != items_container->children().cend());
-    for (size_t i = 1; i < view_model->view_size(); ++i) {
+    for (int i = 1; i < view_model->view_size(); ++i) {
       ++app_iter;
       ASSERT_NE(items_container->children().cend(), app_iter);
       EXPECT_EQ(view_model->view_at(i), *app_iter);
@@ -1026,7 +1024,7 @@
   // Four items should have a layer copy used for animating between rows.
   EXPECT_EQ(4, GetNumberOfRowChangeLayersForTest());
 
-  for (size_t i = 1; i < view_model->view_size(); i++) {
+  for (int i = 1; i < view_model->view_size(); i++) {
     AppListItemView* item_view = view_model->view_at(i);
     // The first item and items off screen on the second page should not
     // animate.
@@ -1385,33 +1383,33 @@
   AppsGridView* items_grid_view = app_list_folder_view()->items_grid_view();
   AppsGridViewTestApi folder_grid_test_api(items_grid_view);
   test_api_->PressItemAt(0);
-  EXPECT_EQ(2u, items_grid_view->view_model()->view_size());
+  EXPECT_EQ(2, items_grid_view->view_model()->view_size());
   EXPECT_EQ(2, items_grid_view->cols());
-  EXPECT_EQ(2u, folder_grid_test_api.TilesPerPage(0));
+  EXPECT_EQ(2, folder_grid_test_api.TilesPerPage(0));
   app_list_folder_view()->CloseFolderPage();
 
   test_api_->PressItemAt(1);
-  EXPECT_EQ(5u, items_grid_view->view_model()->view_size());
+  EXPECT_EQ(5, items_grid_view->view_model()->view_size());
   EXPECT_EQ(3, items_grid_view->cols());
-  EXPECT_EQ(6u, folder_grid_test_api.TilesPerPage(0));
+  EXPECT_EQ(6, folder_grid_test_api.TilesPerPage(0));
   app_list_folder_view()->CloseFolderPage();
 
   test_api_->PressItemAt(2);
-  EXPECT_EQ(9u, items_grid_view->view_model()->view_size());
+  EXPECT_EQ(9, items_grid_view->view_model()->view_size());
   EXPECT_EQ(3, items_grid_view->cols());
-  EXPECT_EQ(9u, folder_grid_test_api.TilesPerPage(0));
+  EXPECT_EQ(9, folder_grid_test_api.TilesPerPage(0));
   app_list_folder_view()->CloseFolderPage();
 
   test_api_->PressItemAt(3);
-  EXPECT_EQ(15u, items_grid_view->view_model()->view_size());
+  EXPECT_EQ(15, items_grid_view->view_model()->view_size());
   EXPECT_EQ(4, items_grid_view->cols());
-  EXPECT_EQ(16u, folder_grid_test_api.TilesPerPage(0));
+  EXPECT_EQ(16, folder_grid_test_api.TilesPerPage(0));
   app_list_folder_view()->CloseFolderPage();
 
   test_api_->PressItemAt(4);
-  EXPECT_EQ(17u, items_grid_view->view_model()->view_size());
+  EXPECT_EQ(17, items_grid_view->view_model()->view_size());
   EXPECT_EQ(4, items_grid_view->cols());
-  EXPECT_EQ(features::IsProductivityLauncherEnabled() ? 20u : 16u,
+  EXPECT_EQ(features::IsProductivityLauncherEnabled() ? 20 : 16,
             folder_grid_test_api.TilesPerPage(0));
   app_list_folder_view()->CloseFolderPage();
 }
@@ -1928,8 +1926,7 @@
   EXPECT_EQ(4, folder_apps_grid_view()->cols());
   // Productivity launcher uses scrollable folders, not paged.
   if (!features::IsProductivityLauncherEnabled()) {
-    EXPECT_EQ(16u,
-              AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
+    EXPECT_EQ(16, AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
     EXPECT_EQ(1, GetTotalPages(folder_apps_grid_view()));
     EXPECT_EQ(0, GetSelectedPage(folder_apps_grid_view()));
   }
@@ -2196,7 +2193,7 @@
 
   EXPECT_EQ(1, GetSelectedPage(folder_apps_grid_view()));
   EXPECT_EQ(4, folder_apps_grid_view()->cols());
-  EXPECT_EQ(16u, AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
+  EXPECT_EQ(16, AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
   EXPECT_TRUE(folder_apps_grid_view()->IsInFolder());
 }
 
@@ -3519,7 +3516,7 @@
   ASSERT_TRUE(reparented_item_view->item());
 
   std::string reparented_item_id = reparented_item_view->item()->id();
-  EXPECT_EQ("Item " + base::NumberToString(GetTilesPerPage(0) - 2),
+  EXPECT_EQ(base::StringPrintf("Item %d", GetTilesPerPage(0) - 2),
             reparented_item_id);
   ASSERT_TRUE(reparented_item_view->HasFocus());
 
@@ -3883,7 +3880,7 @@
   ASSERT_TRUE(paged_apps_grid_view_);
 
   // Create 2 full pages of apps, and add another app to overflow to third page.
-  const size_t kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) + 1;
+  const int kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) + 1;
   model_->PopulateApps(kTotalApps);
   EXPECT_EQ(3, GetPaginationModel()->total_pages());
 
@@ -3901,7 +3898,7 @@
   ASSERT_TRUE(paged_apps_grid_view_);
 
   // Create 2 full pages of apps, and add another app to overflow to third page.
-  const size_t kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) - 1;
+  const int kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) - 1;
   model_->PopulateApps(kTotalApps);
   EXPECT_EQ(2, GetPaginationModel()->total_pages());
 
@@ -3920,7 +3917,7 @@
   UpdateDisplay("1024x768/r");
 
   // Create 2 full pages of apps, and add another app to overflow to third page.
-  const size_t kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) + 1;
+  const int kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) + 1;
   model_->PopulateApps(kTotalApps);
   EXPECT_EQ(3, GetPaginationModel()->total_pages());
 
@@ -3939,7 +3936,7 @@
   UpdateDisplay("1024x768/r");
 
   // Create 2 full pages of apps, and add another app to overflow to third page.
-  const size_t kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) - 1;
+  const int kTotalApps = GetTilesPerPage(0) + GetTilesPerPage(1) - 1;
   model_->PopulateApps(kTotalApps);
   EXPECT_EQ(2, GetPaginationModel()->total_pages());
 
@@ -4067,7 +4064,7 @@
   // Leave the dragged item as a single folder child.
   model_->DeleteItem("Item 1");
   // One folder and one app. Therefore the top level view count is 2.
-  EXPECT_EQ(2u, apps_grid_view_->view_model()->view_size());
+  EXPECT_EQ(2, apps_grid_view_->view_model()->view_size());
 
   // Open the folder.
   test_api_->PressItemAt(0);
@@ -4097,7 +4094,7 @@
 
   // The folder should be deleted. The first item should be Item 2, the second
   // item should be Item 0.
-  EXPECT_EQ(2u, apps_grid_view_->view_model()->view_size());
+  EXPECT_EQ(2, apps_grid_view_->view_model()->view_size());
   EXPECT_EQ("Item 2", GetItemViewInTopLevelGrid(0)->item()->id());
   EXPECT_EQ("Item 0", GetItemViewInTopLevelGrid(1)->item()->id());
 
@@ -4552,7 +4549,7 @@
   TestAppListItemViewIndice();
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view_->view_model();
-  EXPECT_EQ(2u, view_model->view_size());
+  EXPECT_EQ(2, view_model->view_size());
   EXPECT_EQ(view_model->view_at(0),
             test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
   EXPECT_EQ("Item 0", view_model->view_at(0)->item()->id());
@@ -4577,7 +4574,7 @@
             model_->top_level_item_list()->item_at(0)->GetItemType());
   EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
   EXPECT_EQ(4, folder_apps_grid_view()->cols());
-  EXPECT_EQ(16u, AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
+  EXPECT_EQ(16, AppsGridViewTestApi(folder_apps_grid_view()).TilesPerPage(0));
   EXPECT_EQ(1, GetTotalPages(folder_apps_grid_view()));
   EXPECT_EQ(0, GetSelectedPage(folder_apps_grid_view()));
   EXPECT_TRUE(folder_apps_grid_view()->IsInFolder());
@@ -4604,7 +4601,7 @@
   EXPECT_EQ(1, GetPaginationModel()->selected_page());
   EXPECT_EQ(2, GetPaginationModel()->total_pages());
   TestAppListItemViewIndice();
-  EXPECT_EQ(2u, view_model->view_size());
+  EXPECT_EQ(2, view_model->view_size());
   EXPECT_EQ(view_model->view_at(0),
             test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
   EXPECT_EQ("Item 1", view_model->view_at(0)->item()->id());
@@ -4644,7 +4641,7 @@
   EXPECT_EQ("1,0", page_flip_waiter_->selected_pages());
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
-  EXPECT_EQ(1u, view_model->view_size());
+  EXPECT_EQ(1, view_model->view_size());
   EXPECT_EQ(view_model->view_at(0),
             test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
   const AppListItem* folder_item = view_model->view_at(0)->item();
@@ -4684,7 +4681,7 @@
   EXPECT_EQ("1,0", page_flip_waiter_->selected_pages());
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
-  EXPECT_EQ(2u, view_model->view_size());
+  EXPECT_EQ(2, view_model->view_size());
   EXPECT_EQ(view_model->view_at(0),
             test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
   EXPECT_EQ("Item 1", view_model->view_at(0)->item()->id());
@@ -4724,7 +4721,7 @@
   EXPECT_EQ("1,0", page_flip_waiter_->selected_pages());
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
-  EXPECT_EQ(1u, view_model->view_size());
+  EXPECT_EQ(1, view_model->view_size());
   EXPECT_EQ(view_model->view_at(0),
             test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
   EXPECT_EQ("Item 0", view_model->view_at(0)->item()->id());
@@ -4750,7 +4747,7 @@
 
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view_->view_model();
-  EXPECT_EQ(2u, view_model->view_size());
+  EXPECT_EQ(2, view_model->view_size());
   TestAppListItemViewIndice();
 
   // Update the model, and verify the apps grid gets updated.
@@ -4766,7 +4763,7 @@
   UpdateLayout();
 
   // Verify that the view model size matches the new model.
-  EXPECT_EQ(7u, view_model->view_size());
+  EXPECT_EQ(7, view_model->view_size());
   TestAppListItemViewIndice();
 
   // Verify that clicking an item activates it.
@@ -4777,7 +4774,7 @@
   LeftClickOn(view_model->view_at(3));
   EXPECT_TRUE(GetAppListTestHelper()->IsInFolderView());
 
-  ASSERT_EQ(5u, folder_apps_grid_view()->view_model()->view_size());
+  ASSERT_EQ(5, folder_apps_grid_view()->view_model()->view_size());
 
   // Click on an item within the folder.
   LeftClickOn(folder_apps_grid_view()->view_model()->view_at(1));
@@ -4788,14 +4785,14 @@
       /*profile_id=*/1, model_.get(), search_model_.get());
   UpdateLayout();
   EXPECT_FALSE(GetAppListTestHelper()->IsInFolderView());
-  EXPECT_EQ(2u, view_model->view_size());
+  EXPECT_EQ(2, view_model->view_size());
   TestAppListItemViewIndice();
 
   LeftClickOn(view_model->view_at(1));
   EXPECT_EQ("Item 1", GetTestAppListClient()->activate_item_last_id());
 
   Shell::Get()->app_list_controller()->ClearActiveModel();
-  EXPECT_EQ(0u, view_model->view_size());
+  EXPECT_EQ(0, view_model->view_size());
 }
 
 TEST_P(AppsGridViewClamshellAndTabletTest,
@@ -4805,7 +4802,7 @@
   model_->CreateAndPopulateFolderWithApps(5);
   // `GetTilesPerPage()` may return a large number for bubble launcher - ensure
   // the number of test apps is not excessive.
-  model_->PopulateApps(std::min(size_t{30}, GetTilesPerPage(0)));
+  model_->PopulateApps(std::min(30, GetTilesPerPage(0)));
   UpdateLayout();
 
   // Open the folder view.
@@ -4868,7 +4865,7 @@
   model_->CreateAndPopulateFolderWithApps(5);
   // `GetTilesPerPage()` may return a large number for bubble launcher - ensure
   // the number of test apps is not excessive.
-  model_->PopulateApps(std::min(size_t{30}, GetTilesPerPage(0)));
+  model_->PopulateApps(std::min(30, GetTilesPerPage(0)));
   UpdateLayout();
 
   // Open the folder view.
@@ -4931,7 +4928,7 @@
 // ProductivityLauncher feature.
 TEST_P(AppsGridViewDragNonBubbleTest, PageBreakItemAddedAfterDrag) {
   // There are two pages and last item is on second page.
-  const size_t kApps = 2 + GetTilesPerPage(0);
+  const int kApps = 2 + GetTilesPerPage(0);
   model_->PopulateApps(kApps);
   GetPaginationModel()->SelectPage(1, false);
   InitiateDragForItemAtCurrentPageAt(AppsGridView::MOUSE, 0, 1,
@@ -4945,7 +4942,7 @@
 
   // A "page break" item is added to split the pages.
   std::string model_content = "Item " + base::NumberToString(kApps - 1);
-  for (size_t i = 1; i < kApps; ++i) {
+  for (int i = 1; i < kApps; ++i) {
     model_content.append(",Item " + base::NumberToString(i - 1));
     if (i == GetTilesPerPage(0) - 1)
       model_content.append(",PageBreakItem");
@@ -4955,7 +4952,7 @@
 
 TEST_P(AppsGridViewTabletTest, MoveItemToPreviousFullPage) {
   // There are two pages and last item is on second page.
-  const size_t kApps = 2 + GetTilesPerPage(0);
+  const int kApps = 2 + GetTilesPerPage(0);
   model_->PopulateApps(kApps);
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view_->view_model();
@@ -4977,7 +4974,7 @@
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
   EXPECT_EQ(kApps, view_model->view_size());
-  for (size_t i = 0; i < kApps; ++i) {
+  for (int i = 0; i < kApps; ++i) {
     int page = i / GetTilesPerPage(0);
     int slot = i % GetTilesPerPage(0);
     EXPECT_EQ(view_model->view_at(i),
@@ -4992,7 +4989,7 @@
 // ProductivityLauncher feature.
 TEST_P(AppsGridViewDragNonBubbleTest, MoveItemSubsequentDragKeepPageBreak) {
   // There are two pages and last item is on second page.
-  const size_t kApps = 2 + GetTilesPerPage(0);
+  const int kApps = 2 + GetTilesPerPage(0);
   model_->PopulateApps(kApps);
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view_->view_model();
@@ -5019,9 +5016,9 @@
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
   EXPECT_EQ(kApps, view_model->view_size());
-  for (size_t i = 0; i < kApps; ++i) {
-    size_t page = i / GetTilesPerPage(0);
-    size_t slot = i % GetTilesPerPage(0);
+  for (int i = 0; i < kApps; ++i) {
+    int page = i / GetTilesPerPage(0);
+    int slot = i % GetTilesPerPage(0);
     EXPECT_EQ(view_model->view_at(i),
               test_api_->GetViewAtVisualIndex(page, slot));
     EXPECT_EQ("Item " + base::NumberToString((i + kApps - 2) % kApps),
@@ -5030,7 +5027,7 @@
   // A "page break" item still exists.
   std::string model_content = "Item " + base::NumberToString(kApps - 2) +
                               ",Item " + base::NumberToString(kApps - 1);
-  for (size_t i = 2; i < kApps; ++i) {
+  for (int i = 2; i < kApps; ++i) {
     model_content.append(",Item " + base::NumberToString(i - 2));
     if (i == GetTilesPerPage(0) - 1)
       model_content.append(",PageBreakItem");
@@ -5765,18 +5762,18 @@
 TEST_F(AppsGridViewTest, PulsingBlocksShowDuringAppListSync) {
   model_->PopulateApps(3);
   UpdateLayout();
-  EXPECT_EQ(0u, GetPulsingBlocksModel().view_size());
+  EXPECT_EQ(0, GetPulsingBlocksModel().view_size());
 
   // Set the model status as syncing. The Pulsing blocks model should not be
   // empty.
   model_->SetStatus(AppListModelStatus::kStatusSyncing);
   UpdateLayout();
-  EXPECT_NE(0u, GetPulsingBlocksModel().view_size());
+  EXPECT_NE(0, GetPulsingBlocksModel().view_size());
 
   // Set the model status as normal. The Pulsing blocks model should be empty.
   model_->SetStatus(AppListModelStatus::kStatusNormal);
   UpdateLayout();
-  EXPECT_EQ(0u, GetPulsingBlocksModel().view_size());
+  EXPECT_EQ(0, GetPulsingBlocksModel().view_size());
 }
 
 }  // namespace test
diff --git a/ash/app_list/views/paged_apps_grid_view.cc b/ash/app_list/views/paged_apps_grid_view.cc
index 1206967..171567f 100644
--- a/ash/app_list/views/paged_apps_grid_view.cc
+++ b/ash/app_list/views/paged_apps_grid_view.cc
@@ -547,7 +547,7 @@
   }
 
   CalculateIdealBounds();
-  for (size_t i = 0; i < view_model()->view_size(); ++i) {
+  for (int i = 0; i < view_model()->view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
     view->SetBoundsRect(view_model()->ideal_bounds(i));
   }
@@ -704,9 +704,9 @@
 
   // Folders have the same number of tiles on every page, while the root
   // level grid can have a different number of tiles per page.
-  size_t tiles = view_model()->view_size();
+  int tiles = view_model()->view_size();
   int total_pages = 1;
-  size_t tiles_on_page = TilesPerPage(0);
+  int tiles_on_page = TilesPerPage(0);
   while (tiles > tiles_on_page) {
     tiles -= tiles_on_page;
     ++total_pages;
@@ -1180,7 +1180,7 @@
   RecenterItemsContainer();
 
   // Drag view can be nullptr or moved from the model by EndDrag.
-  const size_t number_of_views_to_animate = view_model()->view_size();
+  const int number_of_views_to_animate = view_model()->view_size();
 
   base::RepeatingClosure on_bounds_animator_callback;
   if (number_of_views_to_animate > 0) {
@@ -1211,7 +1211,7 @@
 
   gfx::Vector2d translate_offset(
       0, start_position.y() - items_container()->origin().y());
-  for (size_t i = 0; i < view_model()->view_size(); ++i) {
+  for (int i = 0; i < view_model()->view_size(); ++i) {
     AppListItemView* entry_view = view_model()->view_at(i);
     // Reposition view bounds to compensate for the translation offset.
     gfx::Rect current_bounds = entry_view->bounds();
diff --git a/ash/app_list/views/paged_apps_grid_view_unittest.cc b/ash/app_list/views/paged_apps_grid_view_unittest.cc
index 028e8d000..1f46cbc9 100644
--- a/ash/app_list/views/paged_apps_grid_view_unittest.cc
+++ b/ash/app_list/views/paged_apps_grid_view_unittest.cc
@@ -656,7 +656,7 @@
 
   // Item views should animate upwards to take the place of the closed reorder
   // toast.
-  for (size_t i = 1; i < view_model->view_size(); i++) {
+  for (int i = 1; i < view_model->view_size(); i++) {
     AppListItemView* item_view = view_model->view_at(i);
     // The items off screen on the second page should not animate.
     if (i >= grid_test_api_->TilesPerPage(0)) {
diff --git a/ash/app_list/views/scrollable_apps_grid_view.cc b/ash/app_list/views/scrollable_apps_grid_view.cc
index 58c5821..fc10bb0 100644
--- a/ash/app_list/views/scrollable_apps_grid_view.cc
+++ b/ash/app_list/views/scrollable_apps_grid_view.cc
@@ -86,7 +86,7 @@
   items_container()->SetBoundsRect(GetContentsBounds());
 
   CalculateIdealBounds();
-  for (size_t i = 0; i < view_model()->view_size(); ++i) {
+  for (int i = 0; i < view_model()->view_size(); ++i) {
     AppListItemView* view = GetItemViewAt(i);
     view->SetBoundsRect(view_model()->ideal_bounds(i));
   }
@@ -119,7 +119,7 @@
 
 gfx::Size ScrollableAppsGridView::GetTileGridSize() const {
   // AppListItemList may contain page break items, so use the view_model().
-  size_t items = view_model()->view_size();
+  int items = view_model()->view_size();
   // Tests sometimes start with 0 items. Ensure space for at least 1 item.
   if (items == 0) {
     items = 1;
@@ -313,7 +313,7 @@
   absl::optional<int> first_invisible_row;
 
   const gfx::Rect scroll_view_visible_rect = scroll_view_->GetVisibleRect();
-  for (size_t view_index = 0; view_index < view_model()->view_size();
+  for (int view_index = 0; view_index < view_model()->view_size();
        view_index += cols()) {
     // Calculate an item view's bounds in the scroll content's coordinates.
     gfx::Point item_view_local_origin;
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index 9c2811f..860e31b 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -37,6 +37,8 @@
     "arc_vm_client_adapter.h",
     "file_system_status.cc",
     "file_system_status.h",
+    "serial_number_util.cc",
+    "serial_number_util.h",
   ]
 
   deps = [
@@ -114,6 +116,7 @@
     "connection_holder_unittest.cc",
     "connection_notifier_unittest.cc",
     "file_system_status_unittest.cc",
+    "serial_number_util_unittest.cc",
   ]
 
   deps = [
diff --git a/ash/components/arc/session/serial_number_util.cc b/ash/components/arc/session/serial_number_util.cc
new file mode 100644
index 0000000..40639a6d
--- /dev/null
+++ b/ash/components/arc/session/serial_number_util.cc
@@ -0,0 +1,99 @@
+// 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 "ash/components/arc/session/serial_number_util.h"
+
+#include "ash/components/arc/arc_prefs.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/random.h"
+#include "crypto/sha2.h"
+
+namespace arc {
+
+namespace {
+
+constexpr const size_t kArcSaltFileSize = 16;
+
+// Returns true if the hex-encoded salt in Local State is valid.
+bool IsValidHexSalt(base::StringPiece hex_salt) {
+  std::string salt;
+  if (!base::HexStringToString(hex_salt, &salt)) {
+    LOG(WARNING) << "Not a hex string: " << hex_salt;
+    return false;
+  }
+  if (salt.size() != kArcSaltFileSize) {
+    LOG(WARNING) << "Salt size invalid: " << salt.size();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+std::string GenerateFakeSerialNumber(base::StringPiece chromeos_user,
+                                     base::StringPiece salt) {
+  constexpr size_t kMaxHardwareIdLen = 20;
+  std::string input(chromeos_user);
+  input.append(salt.begin(), salt.end());
+  const std::string hash(crypto::SHA256HashString(input));
+  return base::HexEncode(hash.data(), hash.length())
+      .substr(0, kMaxHardwareIdLen);
+}
+
+std::string GetOrCreateSerialNumber(PrefService* local_state,
+                                    base::StringPiece chromeos_user,
+                                    base::StringPiece arc_salt_on_disk) {
+  DCHECK(local_state);
+  DCHECK(!chromeos_user.empty());
+
+  std::string hex_salt = local_state->GetString(prefs::kArcSerialNumberSalt);
+  if (hex_salt.empty() || !IsValidHexSalt(hex_salt)) {
+    // This path is taken 1) on the very first ARC boot, 2) on the first boot
+    // after powerwash, 3) on the first boot after upgrading to ARCVM, or 4)
+    // when the salt in local state is corrupted.
+    if (arc_salt_on_disk.empty()) {
+      // The device doesn't have the salt file for ARC container. Create it from
+      // scratch in the same way as ARC container.
+      char rand_value[kArcSaltFileSize];
+      crypto::RandBytes(rand_value, kArcSaltFileSize);
+      hex_salt = base::HexEncode(rand_value, kArcSaltFileSize);
+    } else {
+      // The device has the one for container. Reuse it for ARCVM.
+      DCHECK_EQ(kArcSaltFileSize, arc_salt_on_disk.size());
+      hex_salt =
+          base::HexEncode(arc_salt_on_disk.data(), arc_salt_on_disk.size());
+    }
+    local_state->SetString(prefs::kArcSerialNumberSalt, hex_salt);
+  }
+
+  // We store hex-encoded version of the salt in the local state, but to compute
+  // the serial number, we use the decoded version to be compatible with the
+  // arc-setup code for P.
+  std::string decoded_salt;
+  const bool result = base::HexStringToString(hex_salt, &decoded_salt);
+  DCHECK(result) << hex_salt;
+  return GenerateFakeSerialNumber(chromeos_user, decoded_salt);
+}
+
+absl::optional<std::string> ReadSaltOnDisk(const base::FilePath& salt_path) {
+  if (!base::PathExists(salt_path)) {
+    VLOG(2) << "ARC salt file doesn't exist: " << salt_path;
+    return std::string();
+  }
+  std::string salt;
+  if (!base::ReadFileToString(salt_path, &salt)) {
+    PLOG(ERROR) << "Failed to read " << salt_path;
+    return absl::nullopt;
+  }
+  if (salt.size() != kArcSaltFileSize) {
+    LOG(WARNING) << "Ignoring invalid ARC salt on disk. size=" << salt.size();
+    salt.clear();
+  }
+  VLOG(1) << "Successfully read ARC salt on disk: " << salt_path;
+  return salt;
+}
+
+}  // namespace arc
diff --git a/ash/components/arc/session/serial_number_util.h b/ash/components/arc/session/serial_number_util.h
new file mode 100644
index 0000000..047cd89
--- /dev/null
+++ b/ash/components/arc/session/serial_number_util.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef ASH_COMPONENTS_ARC_SESSION_SERIAL_NUMBER_UTIL_H_
+#define ASH_COMPONENTS_ARC_SESSION_SERIAL_NUMBER_UTIL_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "components/prefs/pref_service.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace arc {
+
+// Generates a unique, 20-character hex string from |chromeos_user| and
+// |salt| which can be used as Android's ro.boot.serialno and ro.serialno
+// properties. Note that Android treats serialno in a case-insensitive manner.
+// |salt| cannot be the hex-encoded one.
+// Note: The function must be the exact copy of the one in platform2/arc/setup/.
+std::string GenerateFakeSerialNumber(base::StringPiece chromeos_user,
+                                     base::StringPiece salt);
+
+// Generates and returns a serial number from the salt in |local_state| and
+// |chromeos_user|. When |local_state| does not have it (or has a corrupted
+// one), this function creates a new random salt. When creates it, the function
+// copies |arc_salt_on_disk| to |local_state| if |arc_salt_on_disk| is not
+// empty.
+std::string GetOrCreateSerialNumber(PrefService* local_state,
+                                    base::StringPiece chromeos_user,
+                                    base::StringPiece arc_salt_on_disk);
+
+// Reads a salt from |salt_path| and returns it. Returns a non-null value when
+// the file read is successful or the file does not exist.
+absl::optional<std::string> ReadSaltOnDisk(const base::FilePath& salt_path);
+
+}  // namespace arc
+
+#endif  // ASH_COMPONENTS_ARC_SESSION_SERIAL_NUMBER_UTIL_H_
diff --git a/ash/components/arc/session/serial_number_util_unittest.cc b/ash/components/arc/session/serial_number_util_unittest.cc
new file mode 100644
index 0000000..7c21a9e
--- /dev/null
+++ b/ash/components/arc/session/serial_number_util_unittest.cc
@@ -0,0 +1,290 @@
+// 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 "ash/components/arc/session/serial_number_util.h"
+
+#include "ash/components/arc/arc_prefs.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+
+namespace {
+
+class SerialNumberUtilTest : public testing::Test {
+ public:
+  SerialNumberUtilTest() {
+    arc::prefs::RegisterLocalStatePrefs(test_local_state_.registry());
+  }
+
+  SerialNumberUtilTest(const SerialNumberUtilTest&) = delete;
+  SerialNumberUtilTest& operator=(const SerialNumberUtilTest&) = delete;
+
+  ~SerialNumberUtilTest() override = default;
+
+ protected:
+  PrefService* test_local_state() { return &test_local_state_; }
+
+ private:
+  TestingPrefServiceSimple test_local_state_;
+};
+
+TEST_F(SerialNumberUtilTest, GenerateFakeSerialNumber) {
+  // Check that the function always returns 20-character string.
+  EXPECT_EQ(20U,
+            GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc")
+                .size());
+  EXPECT_EQ(20U, GenerateFakeSerialNumber("", "").size());
+  EXPECT_EQ(20U, GenerateFakeSerialNumber("a", "b").size());
+
+  // Check that the function always returns the same ID for the same
+  // account and hwid_raw.
+  const std::string id_1 =
+      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
+  const std::string id_2 =
+      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
+  EXPECT_EQ(id_1, id_2);
+
+  // Generate an ID for a different account but for the same machine.
+  // Check that the ID is not the same as |id_1|.
+  const std::string id_3 =
+      GenerateFakeSerialNumber("mytestaccount2@gmail.com", "001122aabbcc");
+  EXPECT_NE(id_1, id_3);
+
+  // Generate an ID for a different machine but for the same account.
+  // Check that the ID is not the same as |id_1|.
+  const std::string id_4 =
+      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aaddcc");
+  EXPECT_NE(id_1, id_4);
+
+  // Check that the function treats '\0' in |salt| properly.
+  using std::literals::string_literals::operator""s;
+  const std::string id_5 =
+      GenerateFakeSerialNumber("mytestaccount@gmail.com", "a\0b"s);
+  const std::string id_6 =
+      GenerateFakeSerialNumber("mytestaccount@gmail.com", "a\0c"s);
+  EXPECT_NE(id_5, id_6);
+}
+
+TEST_F(SerialNumberUtilTest, GetOrCreateSerialNumber) {
+  constexpr size_t kSerialNumberLen = 20;
+  constexpr size_t kHexSaltLen = 32;
+
+  const std::string chromeos_user = "user@gmail.com";
+  const std::string chromeos_user2 = "user2@gmail.com";
+  ASSERT_TRUE(
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
+
+  // Check that when neither the pref nor the salt file exists, a random salt
+  // is stored in the local state, and a serial number based on the salt is
+  // returned.
+  const std::string serialno_1 =
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
+  EXPECT_FALSE(serialno_1.empty());
+  EXPECT_EQ(kSerialNumberLen, serialno_1.size());
+
+  const std::string salt_1 =
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt_1.empty());
+  EXPECT_EQ(kHexSaltLen, salt_1.size());
+
+  // Calling the function again returns the same serial/salt.
+  EXPECT_EQ(serialno_1, GetOrCreateSerialNumber(test_local_state(),
+                                                chromeos_user, std::string()));
+  EXPECT_EQ(salt_1, test_local_state()->GetString(prefs::kArcSerialNumberSalt));
+
+  // A different user gets a different serial number, but the salt stays the
+  // same.
+  const std::string serialno_2 = GetOrCreateSerialNumber(
+      test_local_state(), chromeos_user2, std::string());
+  EXPECT_FALSE(serialno_2.empty());
+  EXPECT_EQ(kSerialNumberLen, serialno_2.size());
+  EXPECT_NE(serialno_1, serialno_2);
+  EXPECT_EQ(salt_1, test_local_state()->GetString(prefs::kArcSerialNumberSalt));
+
+  // Delete the salt in local state (which is what Chrome OS PowerWash does.)
+  test_local_state()->ClearPref(prefs::kArcSerialNumberSalt);
+  ASSERT_TRUE(
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
+
+  // Generate the salt and serial for |chromeos_user| again. Verify both are
+  // different than the previous ones.
+  const std::string serialno_3 =
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
+  EXPECT_FALSE(serialno_3.empty());
+  EXPECT_EQ(kSerialNumberLen, serialno_3.size());
+  EXPECT_NE(serialno_1, serialno_3);
+
+  const std::string salt_2 =
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt_2.empty());
+  EXPECT_EQ(kHexSaltLen, salt_2.size());
+  EXPECT_NE(salt_1, salt_2);
+
+  // Delete the salt in local state again.
+  test_local_state()->ClearPref(prefs::kArcSerialNumberSalt);
+  ASSERT_TRUE(
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
+
+  // Pass |salt_on_disk| and verify hex-encoded version of the salt is stored
+  // in local state.
+  using std::literals::string_literals::operator""s;
+  const std::string salt_on_disk = "BAADDECAFC0\0FFEE"s;
+  const std::string salt_on_disk_hex =
+      base::HexEncode(salt_on_disk.data(), salt_on_disk.size());
+  const std::string serialno_4 =
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, salt_on_disk);
+  EXPECT_FALSE(serialno_4.empty());
+  EXPECT_EQ(kSerialNumberLen, serialno_4.size());
+  EXPECT_NE(serialno_1, serialno_4);
+
+  const std::string salt_3 =
+      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_EQ(salt_on_disk_hex, salt_3);
+
+  // A different user gets a different serial number, but the salt stays the
+  // same. This time, pass a different salt on disk to verify it's ignored
+  // when a salt already exists in local state.
+  const std::string serialno_5 = GetOrCreateSerialNumber(
+      test_local_state(), chromeos_user2,
+      // Reverse |salt_on_disk| and pass it.
+      std::string(salt_on_disk.rbegin(), salt_on_disk.rend()));
+  EXPECT_FALSE(serialno_5.empty());
+  EXPECT_EQ(kSerialNumberLen, serialno_5.size());
+  EXPECT_NE(serialno_4, serialno_5);
+  // Local state still has the non-reversed one.
+  EXPECT_EQ(salt_on_disk_hex,
+            test_local_state()->GetString(prefs::kArcSerialNumberSalt));
+}
+
+// That shouldn't happen, but verify that the function can recover the state
+// even if local state has an invalid hex salt.
+TEST_F(SerialNumberUtilTest, GetOrCreateSerialNumber_InvalidLocalState) {
+  constexpr size_t kSaltLen = 16;
+  const std::string chromeos_user = "user@gmail.com";
+
+  // Manually set an invalid hex salt in local state, then call
+  // GetOrCreateSerialNumber. Verify the local state is overwritten by a valid
+  // one.
+  const std::string invalid_hex_salt_1 = "THIS_IS_NOT_A_HEX_STRING";
+  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
+                                invalid_hex_salt_1);
+  EXPECT_FALSE(
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
+          .empty());
+  std::string salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt.empty());
+  EXPECT_NE(invalid_hex_salt_1, salt);
+
+  // Do the same with a too short hex salt.
+  const std::string buf(kSaltLen + 1, 'x');
+  const std::string invalid_hex_salt_2 =
+      base::HexEncode(buf.data(), kSaltLen - 1);  // too short
+  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
+                                invalid_hex_salt_2);
+  EXPECT_FALSE(
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
+          .empty());
+  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt.empty());
+  EXPECT_NE(invalid_hex_salt_2, salt);
+
+  // Do the same with a too long one.
+  const std::string invalid_hex_salt_3 =
+      base::HexEncode(buf.data(), kSaltLen + 1);  // too long
+  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
+                                invalid_hex_salt_3);
+  EXPECT_FALSE(
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
+          .empty());
+  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt.empty());
+  EXPECT_NE(invalid_hex_salt_3, salt);
+
+  // Test the valid case too.
+  const std::string valid_hex_salt = base::HexEncode(buf.data(), kSaltLen);
+  test_local_state()->SetString(prefs::kArcSerialNumberSalt, valid_hex_salt);
+  EXPECT_FALSE(
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
+          .empty());
+  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
+  EXPECT_FALSE(salt.empty());
+  EXPECT_EQ(valid_hex_salt, salt);
+}
+
+// Verify that GetOrCreateSerialNumber uses decoded salt when computing the
+// serial number.
+TEST_F(SerialNumberUtilTest, GetOrCreateSerialNumber_SerialNumberComputation) {
+  constexpr size_t kSaltLen = 16;
+  const std::string chromeos_user = "user@gmail.com";
+
+  // Set the |hex_salt| in local state.
+  const std::string hex_salt =
+      base::HexEncode(std::string(kSaltLen, 'x').data(), kSaltLen);
+  test_local_state()->SetString(prefs::kArcSerialNumberSalt, hex_salt);
+
+  // Get a serial number based on the hex salt.
+  const std::string serial_number =
+      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
+  EXPECT_FALSE(serial_number.empty());
+
+  // Directly compute the serial number with the *hex* salt (which
+  // GetOrCreateSerialNumber is NOT supposed to do). Verify the returned
+  // serial number is NOT the same as the one from GetOrCreateSerialNumber.
+  EXPECT_NE(GenerateFakeSerialNumber(chromeos_user, hex_salt), serial_number);
+}
+
+// Tests that ReadSaltOnDisk can read a non-ASCII salt.
+TEST_F(SerialNumberUtilTest, ReadSaltOnDisk) {
+  constexpr int kSaltLen = 16;
+
+  // Verify the function returns a non-null result when the file doesn't exist.
+  absl::optional<std::string> salt =
+      ReadSaltOnDisk(base::FilePath("/nonexistent/path"));
+  EXPECT_TRUE(salt.has_value());
+
+  // Create a valid arc_salt file.
+  using std::literals::string_literals::operator""s;
+  const std::string expected_salt_value = "BAADDECAFC0\0FFEE"s;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath arc_salt_path = temp_dir.GetPath().Append("arc_salt");
+  ASSERT_EQ(kSaltLen, base::WriteFile(arc_salt_path, expected_salt_value.data(),
+                                      expected_salt_value.size()));
+
+  // Verify the function can read the salt file even when the file contains
+  // non-ASCII characters like '\0'.
+  salt = ReadSaltOnDisk(arc_salt_path);
+  ASSERT_TRUE(salt.has_value());
+  EXPECT_EQ(expected_salt_value, salt.value());
+
+  // Change the mode to drop the r bit. Verify the function returns false
+  // when the file exists, but not readable.
+  ASSERT_TRUE(base::SetPosixFilePermissions(arc_salt_path, 0300));
+  salt = ReadSaltOnDisk(arc_salt_path);
+  EXPECT_FALSE(salt.has_value());
+
+  // Create a different salt file that has corrupted data. Verify the function
+  // returns true but an empty |salt|.
+  arc_salt_path = temp_dir.GetPath().Append("arc_salt2");
+  ASSERT_TRUE(base::WriteFile(arc_salt_path,
+                              std::string(kSaltLen - 1, 'x')));  // too short
+  salt = ReadSaltOnDisk(arc_salt_path);
+  ASSERT_TRUE(salt.has_value());
+  EXPECT_TRUE(salt.value().empty());
+
+  arc_salt_path = temp_dir.GetPath().Append("arc_salt3");
+  ASSERT_TRUE(base::WriteFile(arc_salt_path,
+                              std::string(kSaltLen + 1, 'x')));  // too long
+  salt = ReadSaltOnDisk(arc_salt_path);
+  ASSERT_TRUE(salt.has_value());
+  EXPECT_TRUE(salt.value().empty());
+}
+
+}  // namespace
+
+}  // namespace arc
diff --git a/ash/shelf/hotseat_widget_unittest.cc b/ash/shelf/hotseat_widget_unittest.cc
index 83103fa..a810124 100644
--- a/ash/shelf/hotseat_widget_unittest.cc
+++ b/ash/shelf/hotseat_widget_unittest.cc
@@ -516,7 +516,7 @@
                               ->hotseat_widget()
                               ->scrollable_shelf_view()
                               ->shelf_view();
-  EXPECT_EQ(2u, shelf_view->view_model_for_test()->view_size());
+  EXPECT_EQ(2, shelf_view->view_model_for_test()->view_size());
 
   // Show the in-app shelf.
   SwipeUpOnShelf();
@@ -539,7 +539,7 @@
   // 1. Shelf item count decreases by one; and
   // 2. Hotseat is still extended.
   GetEventGenerator()->ReleaseLeftButton();
-  EXPECT_EQ(1u, shelf_view->view_model_for_test()->view_size());
+  EXPECT_EQ(1, shelf_view->view_model_for_test()->view_size());
   EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state());
 }
 
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 87b82ad..c2a1c611 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -288,17 +288,14 @@
 
     // Scrolls to the new page if the focused shelf item is not tappable
     // on the current page.
-    if (new_index < 0) {
+    if (new_index < 0)
       new_index = focusable_views.size() - 1;
-    } else if (static_cast<size_t>(new_index) >= focusable_views.size()) {
+    else if (new_index >= static_cast<int>(focusable_views.size()))
       new_index = 0;
-    } else if (static_cast<size_t>(new_index) <
-               scrollable_shelf_view_->first_tappable_app_index()) {
+    else if (new_index < scrollable_shelf_view_->first_tappable_app_index())
       scrollable_shelf_view_->ScrollToNewPage(/*forward=*/false);
-    } else if (static_cast<size_t>(new_index) >
-               scrollable_shelf_view_->last_tappable_app_index()) {
+    else if (new_index > scrollable_shelf_view_->last_tappable_app_index())
       scrollable_shelf_view_->ScrollToNewPage(/*forward=*/true);
-    }
 
     return focusable_views[new_index];
   }
@@ -1033,13 +1030,8 @@
   // to be removed, i.e. `view_size_before_removal_` is one. In this case,
   // both `first_tappable_app_index_` and `last_tappable_app_index_` are reset
   // to invalid values (see https://crbug.com/1300561).
-  if (view_size_before_removal < 2) {
-    last_tappable_app_index_ = absl::nullopt;
-  } else {
-    last_tappable_app_index_ = std::min(
-        last_tappable_app_index_,
-        absl::make_optional(static_cast<size_t>(view_size_before_removal - 2)));
-  }
+  last_tappable_app_index_ =
+      std::min(last_tappable_app_index_, view_size_before_removal - 2);
   first_tappable_app_index_ =
       std::min(first_tappable_app_index_, last_tappable_app_index_);
 }
@@ -1731,23 +1723,23 @@
 
   // The value returned by CalculateMainAxisScrollDistance() can be casted into
   // an integer without losing precision since the decimal part is zero.
-  const auto tappable_indices = CalculateTappableIconIndices(
+  const std::pair<int, int> tappable_indices = CalculateTappableIconIndices(
       layout_strategy_, CalculateMainAxisScrollDistance());
   first_tappable_app_index_ = tappable_indices.first;
   last_tappable_app_index_ = tappable_indices.second;
 }
 
-std::pair<absl::optional<size_t>, absl::optional<size_t>>
-ScrollableShelfView::CalculateTappableIconIndices(
+std::pair<int, int> ScrollableShelfView::CalculateTappableIconIndices(
     ScrollableShelfView::LayoutStrategy layout_strategy,
     int scroll_distance_on_main_axis) const {
   const auto& visible_views_indices = shelf_view_->visible_views_indices();
 
   if (visible_views_indices.empty() || visible_space_.IsEmpty())
-    return {absl::nullopt, absl::nullopt};
+    return std::pair<int, int>(-1, -1);
 
   if (layout_strategy == ScrollableShelfView::kNotShowArrowButtons) {
-    return {visible_views_indices.front(), visible_views_indices.back()};
+    return std::pair<int, int>(visible_views_indices.front(),
+                               visible_views_indices.back());
   }
 
   const int visible_size = GetShelf()->IsHorizontalAlignment()
@@ -1760,8 +1752,8 @@
   // are on an inactive desk. Therefore, the indices of tappable apps may not be
   // contiguous, so we need to map from a visible view index back to an app
   // index. The below are indices into the |visible_views_indices| vector.
-  size_t first_visible_view_index;
-  size_t last_visible_view_index;
+  int first_visible_view_index = -1;
+  int last_visible_view_index = -1;
   if (layout_strategy == kShowRightArrowButton ||
       layout_strategy == kShowButtons) {
     first_visible_view_index =
@@ -1793,11 +1785,15 @@
             : last_visible_view_index;
   }
 
-  DCHECK_LT(first_visible_view_index, visible_views_indices.size());
-  DCHECK_LT(last_visible_view_index, visible_views_indices.size());
+  DCHECK_GE(first_visible_view_index, 0);
+  DCHECK_LT(first_visible_view_index,
+            static_cast<int>(visible_views_indices.size()));
+  DCHECK_GE(last_visible_view_index, 0);
+  DCHECK_LT(last_visible_view_index,
+            static_cast<int>(visible_views_indices.size()));
 
-  return {visible_views_indices[first_visible_view_index],
-          visible_views_indices[last_visible_view_index]};
+  return std::pair<int, int>(visible_views_indices[first_visible_view_index],
+                             visible_views_indices[last_visible_view_index]);
 }
 
 views::View* ScrollableShelfView::FindFirstFocusableChild() {
@@ -2144,23 +2140,21 @@
   if (during_scroll_animation_)
     return should_count;
 
-  if (!first_tappable_app_index_.has_value() ||
-      !last_tappable_app_index_.has_value()) {
+  if (first_tappable_app_index_ == -1 || last_tappable_app_index_ == -1) {
     // Verify that `first_tappable_app_index_` and `last_tappable_app_index_`
-    // are both illegal. In that case, return early.
-    DCHECK(first_tappable_app_index_ == last_tappable_app_index_);
+    // may be both illegal. In that case, return early.
+    DCHECK_EQ(first_tappable_app_index_, last_tappable_app_index_);
     return false;
   }
 
   // The ink drop needs to be clipped only if |sender| is the app at one of the
   // corners of the shelf. This happens if it is either the first or the last
   // tappable app and no arrow is showing on its side.
-  if (shelf_view_->view_model()->view_at(first_tappable_app_index_.value()) ==
-      sender) {
+  if (shelf_view_->view_model()->view_at(first_tappable_app_index_) == sender) {
     should_count = !(layout_strategy_ == kShowButtons ||
                      layout_strategy_ == kShowLeftArrowButton);
-  } else if (shelf_view_->view_model()->view_at(
-                 last_tappable_app_index_.value()) == sender) {
+  } else if (shelf_view_->view_model()->view_at(last_tappable_app_index_) ==
+             sender) {
     should_count = !(layout_strategy_ == kShowButtons ||
                      layout_strategy_ == kShowRightArrowButton);
   }
diff --git a/ash/shelf/scrollable_shelf_view.h b/ash/shelf/scrollable_shelf_view.h
index 5411168..a10702d 100644
--- a/ash/shelf/scrollable_shelf_view.h
+++ b/ash/shelf/scrollable_shelf_view.h
@@ -22,7 +22,6 @@
 #include "base/callback_helpers.h"
 #include "base/cancelable_callback.h"
 #include "base/time/time.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/views/animation/ink_drop_host_view.h"
 #include "ui/views/context_menu_controller.h"
@@ -150,12 +149,8 @@
   LayoutStrategy layout_strategy_for_test() const { return layout_strategy_; }
   gfx::Vector2dF scroll_offset_for_test() const { return scroll_offset_; }
 
-  absl::optional<size_t> first_tappable_app_index() const {
-    return first_tappable_app_index_;
-  }
-  absl::optional<size_t> last_tappable_app_index() const {
-    return last_tappable_app_index_;
-  }
+  int first_tappable_app_index() const { return first_tappable_app_index_; }
+  int last_tappable_app_index() const { return last_tappable_app_index_; }
 
   void set_default_last_focusable_child(bool default_last_focusable_child) {
     default_last_focusable_child_ = default_last_focusable_child;
@@ -366,9 +361,9 @@
   // layout strategy and offset along the main axis (that is the x-axis when
   // shelf is horizontally aligned or the y-axis if the shelf is vertically
   // aligned).
-  std::pair<absl::optional<size_t>, absl::optional<size_t>>
-  CalculateTappableIconIndices(LayoutStrategy layout_strategy,
-                               int scroll_distance_on_main_axis) const;
+  std::pair<int, int> CalculateTappableIconIndices(
+      LayoutStrategy layout_strategy,
+      int scroll_distance_on_main_axis) const;
 
   views::View* FindFirstFocusableChild();
   views::View* FindLastFocusableChild();
@@ -515,8 +510,8 @@
   std::unique_ptr<views::FocusSearch> focus_search_;
 
   // The index of the first/last tappable app index.
-  absl::optional<size_t> first_tappable_app_index_ = absl::nullopt;
-  absl::optional<size_t> last_tappable_app_index_ = absl::nullopt;
+  int first_tappable_app_index_ = -1;
+  int last_tappable_app_index_ = -1;
 
   // The number of corner buttons whose ink drop is activated.
   int activated_corner_buttons_ = 0;
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 10df04c..fce7116 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -173,8 +173,8 @@
     GetEventGenerator()->MoveMouseTo(
         GetPrimaryDisplay().bounds().CenterPoint());
 
-    for (size_t i = scrollable_shelf_view_->first_tappable_app_index().value();
-         i <= scrollable_shelf_view_->last_tappable_app_index().value(); ++i) {
+    for (int i = scrollable_shelf_view_->first_tappable_app_index();
+         i <= scrollable_shelf_view_->last_tappable_app_index(); ++i) {
       const auto* shelf_app_icon = test_api_->GetButton(i);
       const gfx::Rect app_icon_screen_bounds =
           shelf_app_icon->GetBoundsInScreen();
@@ -205,16 +205,16 @@
     views::View::ConvertRectToScreen(scrollable_shelf_view_,
                                      &visible_space_in_screen);
 
-    views::View* last_tappable_icon = view_model->view_at(
-        scrollable_shelf_view_->last_tappable_app_index().value());
+    views::View* last_tappable_icon =
+        view_model->view_at(scrollable_shelf_view_->last_tappable_app_index());
     const gfx::Rect last_tappable_icon_bounds =
         last_tappable_icon->GetBoundsInScreen();
 
     // Expects that the last tappable icon is fully shown.
     EXPECT_TRUE(visible_space_in_screen.Contains(last_tappable_icon_bounds));
 
-    views::View* first_tappable_icon = view_model->view_at(
-        scrollable_shelf_view_->first_tappable_app_index().value());
+    views::View* first_tappable_icon =
+        view_model->view_at(scrollable_shelf_view_->first_tappable_app_index());
     const gfx::Rect first_tappable_icon_bounds =
         first_tappable_icon->GetBoundsInScreen();
 
@@ -328,8 +328,8 @@
   EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
             scrollable_shelf_view_->layout_strategy_for_test());
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
-  const views::View* last_visible_icon = view_model->view_at(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+  const views::View* last_visible_icon =
+      view_model->view_at(scrollable_shelf_view_->last_tappable_app_index());
   const gfx::Rect icon_bounds = last_visible_icon->GetBoundsInScreen();
   gfx::Rect visible_space = scrollable_shelf_view_->visible_space();
   views::View::ConvertRectToScreen(scrollable_shelf_view_, &visible_space);
@@ -396,6 +396,7 @@
 
   ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
             scrollable_shelf_view_->layout_strategy_for_test());
+  scrollable_shelf_view_->first_tappable_app_index();
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
 
@@ -404,8 +405,8 @@
   EXPECT_FALSE(tooltip_manager->IsVisible());
 
   // Verifies that tooltip should show for a visible shelf item.
-  views::View* visible_icon = view_model->view_at(
-      scrollable_shelf_view_->first_tappable_app_index().value());
+  views::View* visible_icon =
+      view_model->view_at(scrollable_shelf_view_->first_tappable_app_index());
   GetEventGenerator()->MoveMouseTo(
       visible_icon->GetBoundsInScreen().CenterPoint());
   tooltip_manager->ShowTooltip(visible_icon);
@@ -418,7 +419,7 @@
 
   // Verifies that tooltip should not show for a hidden shelf item.
   views::View* hidden_icon = view_model->view_at(
-      scrollable_shelf_view_->last_tappable_app_index().value() + 1);
+      scrollable_shelf_view_->last_tappable_app_index() + 1);
   GetEventGenerator()->MoveMouseTo(
       hidden_icon->GetBoundsInScreen().CenterPoint());
   tooltip_manager->ShowTooltip(hidden_icon);
@@ -557,8 +558,8 @@
             scrollable_shelf_view_->layout_strategy_for_test());
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
-  views::View* dragged_view = view_model->view_at(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+  views::View* dragged_view =
+      view_model->view_at(scrollable_shelf_view_->last_tappable_app_index());
   const gfx::Point drag_start_point =
       dragged_view->GetBoundsInScreen().CenterPoint();
 
@@ -569,7 +570,7 @@
       scrollable_shelf_view_->left_arrow()->GetBoundsInScreen().origin() -
       gfx::Vector2d(10, 0);
 
-  ASSERT_NE(0u, view_model->GetIndexOfView(dragged_view));
+  ASSERT_NE(0, view_model->GetIndexOfView(dragged_view));
 
   // Drag |dragged_view| from |drag_start_point| to |drag_end_point|. Wait
   // for enough time before releasing the mouse button.
@@ -598,19 +599,17 @@
   // (2) The dragged view has the correct view index.
   EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
             scrollable_shelf_view_->layout_strategy_for_test());
-  const auto view_index = view_model->GetIndexOfView(dragged_view);
-  EXPECT_GE(view_index,
-            scrollable_shelf_view_->first_tappable_app_index().value());
-  EXPECT_LE(view_index,
-            scrollable_shelf_view_->last_tappable_app_index().value());
+  const int view_index = view_model->GetIndexOfView(dragged_view);
+  EXPECT_GE(view_index, scrollable_shelf_view_->first_tappable_app_index());
+  EXPECT_LE(view_index, scrollable_shelf_view_->last_tappable_app_index());
 }
 
 // Verifies that after adding the second display, shelf icons showing on
 // the primary display are also visible on the second display
 // (https://crbug.com/1035596).
 TEST_P(ScrollableShelfViewRTLTest, CheckTappableIndicesOnSecondDisplay) {
-  constexpr size_t icon_number = 5;
-  for (size_t i = 0; i < icon_number; i++)
+  constexpr int icon_number = 5;
+  for (int i = 0; i < icon_number; i++)
     AddAppShortcut();
 
   // Adds the second display.
@@ -626,9 +625,8 @@
 
   // Verifies that the all icons are visible on the secondary display.
   EXPECT_EQ(icon_number - 1,
-            secondary_scrollable_shelf_view->last_tappable_app_index().value());
-  EXPECT_EQ(
-      0u, secondary_scrollable_shelf_view->first_tappable_app_index().value());
+            secondary_scrollable_shelf_view->last_tappable_app_index());
+  EXPECT_EQ(0, secondary_scrollable_shelf_view->first_tappable_app_index());
 }
 
 // Verifies that the scrollable shelf in oveflow mode has the correct layout
@@ -665,8 +663,8 @@
   base::RunLoop().RunUntilIdle();
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
-  views::View* first_tappable_view = view_model->view_at(
-      scrollable_shelf_view_->first_tappable_app_index().value());
+  views::View* first_tappable_view =
+      view_model->view_at(scrollable_shelf_view_->first_tappable_app_index());
 
   // Verifies that the gap between the left arrow button and the first tappable
   // icon is expected.
@@ -707,12 +705,10 @@
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
   const gfx::Rect first_tappable_icon_bounds =
-      view_model
-          ->view_at(scrollable_shelf_view_->first_tappable_app_index().value())
+      view_model->view_at(scrollable_shelf_view_->first_tappable_app_index())
           ->GetBoundsInScreen();
   const gfx::Rect last_tappable_icon_bounds =
-      view_model
-          ->view_at(scrollable_shelf_view_->last_tappable_app_index().value())
+      view_model->view_at(scrollable_shelf_view_->last_tappable_app_index())
           ->GetBoundsInScreen();
 
   if (base::i18n::IsRTL()) {
@@ -761,9 +757,9 @@
   ShelfViewTestAPI shelf_view_test_api(shelf_view_);
 
   ShelfAppButton* first_icon = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->first_tappable_app_index().value());
+      scrollable_shelf_view_->first_tappable_app_index());
   ShelfAppButton* last_icon = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+      scrollable_shelf_view_->last_tappable_app_index());
 
   // When the right arrow is showing, check rounded corners are set if the ink
   // drop is visible for the first visible app.
@@ -782,9 +778,9 @@
 
   // Recalculate first and last icons.
   first_icon = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->first_tappable_app_index().value());
+      scrollable_shelf_view_->first_tappable_app_index());
   last_icon = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+      scrollable_shelf_view_->last_tappable_app_index());
 
   // When the left arrow is showing, check rounded corners are set if the ink
   // drop is visible for the last visible app.
@@ -853,7 +849,7 @@
 
   AddAppShortcut();
   const ShelfID app_id = AddAppShortcut();
-  ASSERT_EQ(2u, test_api_->GetButtonCount());
+  ASSERT_EQ(2, test_api_->GetButtonCount());
 
   ShelfModel* shelf_model = ShelfModel::Get();
   const int index = shelf_model->ItemIndexByID(app_id);
@@ -873,7 +869,7 @@
   // Emulate to remove a shelf icon from context menu.
   shelf_model->RemoveItemAt(index);
   test_api_->RunMessageLoopUntilAnimationsDone();
-  ASSERT_EQ(1u, test_api_->GetButtonCount());
+  ASSERT_EQ(1, test_api_->GetButtonCount());
 
   // Verify the count of activated corner buttons.
   EXPECT_FALSE(
@@ -981,8 +977,7 @@
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
   const gfx::Rect first_tappable_view_bounds =
-      view_model
-          ->view_at(scrollable_shelf_view_->first_tappable_app_index().value())
+      view_model->view_at(scrollable_shelf_view_->first_tappable_app_index())
           ->GetBoundsInScreen();
 
   const gfx::Point drag_start_location =
@@ -1200,7 +1195,7 @@
 
   ShelfViewTestAPI shelf_view_test_api(shelf_view_);
   ShelfAppButton* first_app_button = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->first_tappable_app_index().value());
+      scrollable_shelf_view_->first_tappable_app_index());
   VeirifyRippleRingWithinShelfContainer(*first_app_button);
 
   // Tap at the right arrow. Hotseat layout should show the left arrow.
@@ -1211,7 +1206,7 @@
             scrollable_shelf_view_->layout_strategy_for_test());
 
   ShelfAppButton* last_app_button = shelf_view_test_api.GetButton(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+      scrollable_shelf_view_->last_tappable_app_index());
   VeirifyRippleRingWithinShelfContainer(*last_app_button);
 }
 
@@ -1231,7 +1226,7 @@
 
   // Right-click on the edge of the last icon.
   const views::View* last_icon = shelf_view_->view_model_for_test()->view_at(
-      scrollable_shelf_view_->last_tappable_app_index().value());
+      scrollable_shelf_view_->last_tappable_app_index());
   gfx::Point click_point = last_icon->GetBoundsInScreen().right_center();
   click_point.Offset(-1, 0);
   GetEventGenerator()->MoveMouseTo(click_point);
@@ -1284,7 +1279,7 @@
             scrollable_shelf_view_->layout_strategy_for_test());
 
   views::ViewModel* view_model = shelf_view_->view_model_for_test();
-  ASSERT_GT(view_model->view_size(), 2u);
+  ASSERT_GT(view_model->view_size(), 2);
 
   // |gesture_start_point| is between the first and the second shelf icon. It
   // ensures that gesture scroll does not start from a point within any shelf
@@ -1364,14 +1359,14 @@
             scrollable_shelf_view_->layout_strategy_for_test());
 
   // |num| is the minimum of app icons to enter overflow mode.
-  const size_t num = shelf_view_->view_model_for_test()->view_size();
+  const int num = shelf_view_->view_model_for_test()->view_size();
 
   ShelfModel::ScopedUserTriggeredMutation user_triggered(
       scrollable_shelf_view_->shelf_view()->model());
 
   {
     ShelfID shelf_id = AddAppShortcut();
-    const size_t view_index =
+    const int view_index =
         shelf_view_->model()->ItemIndexByAppID(shelf_id.app_id);
     ASSERT_EQ(view_index, num);
 
@@ -1387,22 +1382,20 @@
             scrollable_shelf_view_->layout_strategy_for_test());
 
   // Pins the icons of running apps to the shelf.
-  for (size_t i = 0; i < 2 * num; i++)
+  for (int i = 0; i < 2 * num; i++)
     AddAppShortcut(ShelfItemType::TYPE_APP);
 
   {
     ShelfID shelf_id = AddAppShortcut();
-    const size_t view_index =
+    const int view_index =
         shelf_view_->model()->ItemIndexByAppID(shelf_id.app_id);
     ASSERT_EQ(view_index, num + 1);
 
     // Scrolls the shelf to show the pinned app. Expects that the shelf is
     // scrolled to the correct page. Notes that the pinned app should be placed
     // ahead of running apps.
-    EXPECT_LT(view_index,
-              scrollable_shelf_view_->last_tappable_app_index().value());
-    EXPECT_GT(view_index,
-              scrollable_shelf_view_->first_tappable_app_index().value());
+    EXPECT_LT(view_index, scrollable_shelf_view_->last_tappable_app_index());
+    EXPECT_GT(view_index, scrollable_shelf_view_->first_tappable_app_index());
     EXPECT_EQ(ScrollableShelfView::kShowButtons,
               scrollable_shelf_view_->layout_strategy_for_test());
   }
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index da5a47a..e27964b 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -613,8 +613,7 @@
   // the shelf when there is a non-zero padding between the app icon and the
   // end of scrollable shelf.
   if (TabletModeController::Get()->InTabletMode() && padding > 0) {
-    const size_t current_index =
-        shelf_view_->view_model()->GetIndexOfView(this).value();
+    const int current_index = shelf_view_->view_model()->GetIndexOfView(this);
     int left_padding =
         (shelf_view_->visible_views_indices().front() == current_index)
             ? padding
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 8388a72..65c3a728 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -3074,10 +3074,10 @@
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
 
   // Create an app that we can use to pull up a context menu.
-  EXPECT_EQ(0u,
+  EXPECT_EQ(0,
             ShelfViewTestAPI(shelf->GetShelfViewForTesting()).GetButtonCount());
   AddApp();
-  EXPECT_EQ(1u,
+  EXPECT_EQ(1,
             ShelfViewTestAPI(shelf->GetShelfViewForTesting()).GetButtonCount());
 
   // Swipe up to show the shelf.
@@ -4476,8 +4476,8 @@
 
   // Keep this low so that all apps fit at the center of the screen on all
   // displays.
-  const size_t max_app_count = 4;
-  for (size_t app_count = 1; app_count <= max_app_count; ++app_count) {
+  const int max_app_count = 4;
+  for (int app_count = 1; app_count <= max_app_count; ++app_count) {
     AddApp();
 
     // Wait for everything to settle down.
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index c0da227..6ec00058 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -47,7 +47,7 @@
   views::Widget* GetTooltip() { return tooltip_manager_->bubble_->GetWidget(); }
 
   void ShowTooltipForFirstAppIcon() {
-    EXPECT_GE(shelf_view_->number_of_visible_apps(), 1u);
+    EXPECT_GE(shelf_view_->number_of_visible_apps(), 1);
     tooltip_manager_->ShowTooltip(
         shelf_view_->first_visible_button_for_testing());
   }
diff --git a/ash/shelf/shelf_unittest.cc b/ash/shelf/shelf_unittest.cc
index 90a0745..5e34c9a4 100644
--- a/ash/shelf/shelf_unittest.cc
+++ b/ash/shelf/shelf_unittest.cc
@@ -70,7 +70,7 @@
 // Confirms that ShelfItem reflects the appropriated state.
 TEST_F(ShelfTest, StatusReflection) {
   // Initially we have the app list.
-  size_t button_count = test_api()->GetButtonCount();
+  int button_count = test_api()->GetButtonCount();
 
   // Add a running app.
   ShelfItem item;
@@ -92,7 +92,7 @@
 // browser test we check this here.
 TEST_F(ShelfTest, CheckHoverAfterMenu) {
   // Initially we have the app list.
-  size_t button_count = test_api()->GetButtonCount();
+  int button_count = test_api()->GetButtonCount();
 
   // Add a running app.
   ShelfItem item;
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index d077aac..65d1a5b 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -457,12 +457,10 @@
 
 gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(const ShelfID& id) {
   int index = model_->ItemIndexByID(id);
-  if (index < 0 ||
-      !base::Contains(visible_views_indices_, static_cast<size_t>(index)))
+  if (!base::Contains(visible_views_indices_, index))
     return gfx::Rect();
 
-  const gfx::Rect& ideal_bounds(
-      view_model_->ideal_bounds(static_cast<size_t>(index)));
+  const gfx::Rect& ideal_bounds(view_model_->ideal_bounds(index));
   ShelfAppButton* button = GetShelfAppButton(id);
   gfx::Rect icon_bounds = button->GetIconBounds();
   return gfx::Rect(GetMirroredXWithWidthInView(
@@ -478,7 +476,7 @@
 
 void ShelfView::UpdateVisibleShelfItemBoundsUnion() {
   visible_shelf_item_bounds_union_.SetRect(0, 0, 0, 0);
-  for (const auto i : visible_views_indices_) {
+  for (const int i : visible_views_indices_) {
     const views::View* child = view_model_->view_at(i);
     if (ShouldShowTooltipForChildView(child)) {
       visible_shelf_item_bounds_union_.Union(
@@ -720,11 +718,11 @@
   if (!details.is_add) {
     if (details.child == current_ghost_view_) {
       current_ghost_view_ = nullptr;
-      current_ghost_view_index_ = absl::nullopt;
+      current_ghost_view_index_ = -1;
     }
     if (details.child == last_ghost_view_) {
       last_ghost_view_ = nullptr;
-      current_ghost_view_index_ = absl::nullopt;
+      current_ghost_view_index_ = -1;
     }
   }
 }
@@ -759,7 +757,7 @@
 
   // Record the index for the last pressed shelf item.
   last_pressed_index_ = view_model_->GetIndexOfView(sender);
-  DCHECK(last_pressed_index_.has_value());
+  DCHECK_LT(-1, last_pressed_index_);
 
   // Place new windows on the same display as the button. Opening windows is
   // usually an async operation so we wait until window activation changes
@@ -784,7 +782,7 @@
   }
 
   // Collect usage statistics before we decide what to do with the click.
-  switch (model_->items()[last_pressed_index_.value()].type) {
+  switch (model_->items()[last_pressed_index_].type) {
     case TYPE_PINNED_APP:
     case TYPE_BROWSER_SHORTCUT:
     case TYPE_APP:
@@ -801,7 +799,7 @@
   }
 
   // Run AfterItemSelected directly if the item has no delegate (ie. in tests).
-  const ShelfItem& item = model_->items()[last_pressed_index_.value()];
+  const ShelfItem& item = model_->items()[last_pressed_index_];
   if (!model_->GetShelfItemDelegate(item.id)) {
     AfterItemSelected(item, sender, ui::Event::Clone(event), ink_drop,
                       SHELF_ACTION_NONE, {});
@@ -849,7 +847,7 @@
     }
     return;
   }
-  last_pressed_index_ = absl::nullopt;
+  last_pressed_index_ = -1;
   if (!item || !model_->GetShelfItemDelegate(item->id)) {
     ShowShelfContextMenu(ShelfID(), point, source, source_type, nullptr);
     return;
@@ -882,7 +880,7 @@
 void ShelfView::OnShelfConfigUpdated() {
   // Ensure the shelf app buttons have an icon which is up to date with the
   // current ShelfConfig sizing.
-  for (size_t i = 0; i < view_model_->view_size(); i++) {
+  for (int i = 0; i < view_model_->view_size(); i++) {
     ShelfAppButton* button =
         static_cast<ShelfAppButton*>(view_model_->view_at(i));
     if (!button->IsIconSizeCurrent())
@@ -911,8 +909,8 @@
   const bool repost = IsRepostEvent(event);
 
   // Ignore if this is a repost event on the last pressed shelf item.
-  auto index = view_model_->GetIndexOfView(view);
-  if (!index.has_value())
+  int index = view_model_->GetIndexOfView(view);
+  if (index == -1)
     return false;
   return !repost || last_pressed_index_ != index;
 }
@@ -974,8 +972,7 @@
 }
 
 void ShelfView::CalculateIdealBounds() {
-  DCHECK(static_cast<size_t>(model()->item_count()) ==
-         view_model_->view_size());
+  DCHECK(model()->item_count() == view_model_->view_size());
 
   const int button_spacing = ShelfConfig::Get()->button_spacing();
   UpdateSeparatorIndex();
@@ -984,11 +981,11 @@
 
   // Don't show the separator if it isn't needed, or would appear after all
   // visible items.
-  separator_->SetVisible(separator_index_.has_value() &&
+  separator_->SetVisible(separator_index_ != -1 &&
                          separator_index_ < visible_views_indices_.back());
-  // Set |separator_index_| to nullopt if it is not visible.
+  // Set |separator_index_| to -1 if it is not visible.
   if (!separator_->GetVisible())
-    separator_index_ = absl::nullopt;
+    separator_index_ = -1;
 
   app_icons_layout_offset_ = CalculateAppIconsLayoutOffset();
   int x = shelf()->PrimaryAxisValue(app_icons_layout_offset_, 0);
@@ -997,7 +994,7 @@
   // The padding is handled in ScrollableShelfView.
 
   const int button_size = GetButtonSize();
-  for (size_t i = 0; i < view_model_->view_size(); ++i) {
+  for (int i = 0; i < view_model_->view_size(); ++i) {
     if (view_model_->view_at(i)->GetVisible()) {
       gfx::Rect ideal_view_bounds(x, y, button_size, button_size);
       view_model_->set_ideal_bounds(i, ideal_view_bounds);
@@ -1087,17 +1084,16 @@
 void ShelfView::UpdateSeparatorIndex() {
   // A separator is shown after the last pinned item only if it's followed by a
   // visible app item.
-  absl::optional<size_t> first_unpinned_index = absl::nullopt;
-  absl::optional<size_t> last_pinned_index = absl::nullopt;
+  int first_unpinned_index = -1;
+  int last_pinned_index = -1;
 
-  absl::optional<size_t> dragged_item_index = absl::nullopt;
+  int dragged_item_index = -1;
   if (drag_view_)
     dragged_item_index = view_model_->GetIndexOfView(drag_view_);
 
   const bool can_drag_view_across_separator =
       drag_view_ && CanDragAcrossSeparator(drag_view_);
-  for (size_t index = model()->item_count(); index > 0; --index) {
-    const size_t i = index - 1;
+  for (int i = model()->item_count() - 1; i >= 0; --i) {
     const auto& item = model()->items()[i];
     if (IsItemPinned(item)) {
       // Dragged pinned item may be moved to the unpinned side of the shelf and
@@ -1116,8 +1112,8 @@
 
   // If there is no unpinned item in shelf, return -1 as the separator should be
   // hidden.
-  if (!first_unpinned_index.has_value()) {
-    separator_index_ = absl::nullopt;
+  if (first_unpinned_index == -1) {
+    separator_index_ = -1;
     return;
   }
 
@@ -1165,7 +1161,7 @@
 
   // If the AppsGridView (which was dispatching this event) was opened by our
   // button, ShelfView dragging operations are locked and we have to unlock.
-  CancelDrag(absl::nullopt);
+  CancelDrag(-1);
   drag_and_drop_item_pinned_ = false;
   drag_and_drop_shelf_id_ = ShelfID(app_id);
   // Check if the application is pinned - if not, we have to pin it so
@@ -1280,8 +1276,8 @@
     return;
 
   // Find the index of the button to swap in the view model.
-  size_t src_index = static_cast<size_t>(-1);
-  for (size_t i = 0; i < view_model_->view_size(); ++i) {
+  int src_index = -1;
+  for (int i = 0; i < view_model_->view_size(); ++i) {
     View* view = view_model_->view_at(i);
     if (view == button_to_swap) {
       src_index = i;
@@ -1309,8 +1305,8 @@
   if (IsShowingMenu())
     shelf_menu_model_adapter_->Cancel();
 
-  auto index = view_model_->GetIndexOfView(view);
-  if (!index.has_value() || view_model_->view_size() < 1)
+  int index = view_model_->GetIndexOfView(view);
+  if (index == -1 || view_model_->view_size() < 1)
     return;  // View is being deleted, ignore request.
 
   // Reset drag icon proxy from previous drag (which could be set if the drop
@@ -1355,7 +1351,7 @@
   is_repost_event_on_same_item_ = false;
 
   if (canceled) {
-    CancelDrag(absl::nullopt);
+    CancelDrag(-1);
   } else if (drag_pointer_ == pointer) {
     FinalizeRipOffDrag(false);
     drag_pointer_ = NONE;
@@ -1364,8 +1360,7 @@
     // |drag_view_| is dragged over the separator. Do nothing if |drag_view_| is
     // already handled in FinalizedRipOffDrag.
     if (drag_view_) {
-      if (ShouldUpdateDraggedViewPinStatus(
-              view_model_->GetIndexOfView(view).value())) {
+      if (ShouldUpdateDraggedViewPinStatus(view_model_->GetIndexOfView(view))) {
         const std::string drag_app_id = ShelfItemForView(drag_view_)->id.app_id;
         ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
         if (model_->IsAppPinned(drag_app_id)) {
@@ -1387,8 +1382,8 @@
     drag_icon_proxy_.reset();
 
   if (drag_icon_proxy_) {
-    const gfx::Rect drag_view_ideal_bounds = view_model_->ideal_bounds(
-        view_model_->GetIndexOfView(drag_view_).value());
+    const gfx::Rect drag_view_ideal_bounds =
+        view_model_->ideal_bounds(view_model_->GetIndexOfView(drag_view_));
     gfx::Rect target_bounds_in_screen =
         drag_view_->GetIdealIconBounds(drag_view_ideal_bounds.size(),
                                        /*icon_scale=*/1.0f);
@@ -1454,7 +1449,7 @@
   move_animation_tracker_->Start(metrics_util::ForSmoothness(
       base::BindRepeating(&ReportMoveAnimationSmoothness)));
 
-  for (size_t i = 0; i < view_model_->view_size(); ++i) {
+  for (int i = 0; i < view_model_->view_size(); ++i) {
     View* view = view_model_->view_at(i);
     bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
     // Now that the item animation starts, we have to make sure that the
@@ -1490,8 +1485,8 @@
   start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
   drag_scroll_dir_ = 0;
 
-  if (!start_drag_index_.has_value()) {
-    CancelDrag(absl::nullopt);
+  if (start_drag_index_ == -1) {
+    CancelDrag(-1);
     return;
   }
 
@@ -1537,15 +1532,15 @@
 void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
   DCHECK(dragging());
   DCHECK(drag_view_);
-  const auto index = view_model_->GetIndexOfView(drag_view_);
-  DCHECK(index.has_value());
+  DCHECK_NE(-1, view_model_->GetIndexOfView(drag_view_));
 
   const bool dragged_off_shelf_before = dragged_off_shelf_;
 
   // Handle rip off functionality if this is not a drag and drop host operation
   // and not the app list item.
   if (drag_and_drop_shelf_id_.IsNull() &&
-      RemovableByRipOff(index.value()) != NOT_REMOVABLE) {
+      RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) !=
+          NOT_REMOVABLE) {
     HandleRipOffDrag(event);
     // Check if the item got ripped off the shelf - if it did we are done.
     if (dragged_off_shelf_) {
@@ -1578,16 +1573,13 @@
                        : drag_icon_bounds_in_screen_;
   delegate_->ScheduleScrollForItemDragIfNeeded(drag_icon_bounds_in_screen);
 
-  if (dragged_off_shelf_before) {
-    model_->OnItemReturnedFromRipOff(
-        static_cast<int>(view_model_->GetIndexOfView(drag_view_).value()));
-  }
+  if (dragged_off_shelf_before)
+    model_->OnItemReturnedFromRipOff(view_model_->GetIndexOfView(drag_view_));
 }
 
 void ShelfView::MoveDragViewTo(int primary_axis_coordinate) {
-  const size_t current_item_index =
-      view_model_->GetIndexOfView(drag_view_).value();
-  const std::pair<size_t, size_t> indices(GetDragRange(current_item_index));
+  const int current_item_index = view_model_->GetIndexOfView(drag_view_);
+  const std::pair<int, int> indices(GetDragRange(current_item_index));
   if (shelf_->IsHorizontalAlignment()) {
     int x = GetMirroredXWithWidthInView(primary_axis_coordinate,
                                         drag_view_->width());
@@ -1607,7 +1599,7 @@
       drag_view_->SetY(y);
   }
 
-  size_t target_index = views::ViewModelUtils::DetermineMoveIndex(
+  int target_index = views::ViewModelUtils::DetermineMoveIndex(
       *view_model_, drag_view_, shelf_->IsHorizontalAlignment(),
       drag_view_->x(), drag_view_->y());
   target_index = base::clamp(target_index, indices.first, indices.second);
@@ -1646,9 +1638,9 @@
 }
 
 void ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
-  auto current_index = view_model_->GetIndexOfView(drag_view_);
-  DCHECK(current_index.has_value());
-  std::string dragged_app_id = model_->items()[current_index.value()].id.app_id;
+  int current_index = view_model_->GetIndexOfView(drag_view_);
+  DCHECK_NE(-1, current_index);
+  std::string dragged_app_id = model_->items()[current_index].id.app_id;
 
   aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
   gfx::Point screen_location = event.root_location();
@@ -1699,13 +1691,11 @@
     dragged_off_shelf_ = true;
     RemoveGhostView();
 
-    if (RemovableByRipOff(current_index.value()) == REMOVABLE) {
+    if (RemovableByRipOff(current_index) == REMOVABLE) {
       // Move the item to the back and hide it. ShelfItemMoved() callback will
       // handle the |view_model_| update and call AnimateToIdealBounds().
-      if (current_index.value() !=
-          static_cast<size_t>(model_->item_count() - 1)) {
-        model_->Move(current_index.value(), model_->item_count() - 1);
-      }
+      if (current_index != model_->item_count() - 1)
+        model_->Move(current_index, model_->item_count() - 1);
       // Make the item partially disappear to show that it will get removed if
       // dropped.
       drag_icon_proxy_->SetOpacity(kDraggedImageOpacity);
@@ -1725,11 +1715,11 @@
 
   delegate_->CancelScrollForItemDrag();
 
-  auto current_index = view_model_->GetIndexOfView(drag_view_);
-  // If the view isn't part of the model anymore, a sync operation must have
-  // removed it. In that case we shouldn't change the model and only delete the
-  // proxy image.
-  if (!current_index.has_value()) {
+  int current_index = view_model_->GetIndexOfView(drag_view_);
+  // If the view isn't part of the model anymore (|current_index| == -1), a sync
+  // operation must have removed it. In that case we shouldn't change the model
+  // and only delete the proxy image.
+  if (current_index == -1) {
     drag_icon_proxy_.reset();
     return;
   }
@@ -1738,7 +1728,7 @@
   bool snap_back = false;
   // Items which cannot be dragged off will be handled as a cancel.
   if (!cancel) {
-    if (RemovableByRipOff(current_index.value()) != REMOVABLE) {
+    if (RemovableByRipOff(current_index) != REMOVABLE) {
       // Make sure we do not try to remove un-removable items like items which
       // were not pinned or have to be always there.
       cancel = true;
@@ -1747,7 +1737,7 @@
       // Make sure the item stays invisible upon removal.
       drag_view_->SetVisible(false);
       ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
-      model_->UnpinAppWithID(model_->items()[current_index.value()].id.app_id);
+      model_->UnpinAppWithID(model_->items()[current_index].id.app_id);
     }
   }
   if (cancel || snap_back) {
@@ -1766,7 +1756,7 @@
       drag_view_->AddState(ShelfAppButton::STATE_HIDDEN);
       // When a canceling drag model is happening, the view model is diverged
       // from the menu model and movements / animations should not be done.
-      model_->Move(current_index.value(), start_drag_index_.value());
+      model_->Move(current_index, start_drag_index_);
       AnimateToIdealBounds();
     }
     drag_view_->layer()->SetOpacity(1.0f);
@@ -1815,7 +1805,7 @@
          (!reverse && button == FindFirstFocusableChild());
 }
 
-std::pair<size_t, size_t> ShelfView::GetDragRange(size_t index) {
+std::pair<int, int> ShelfView::GetDragRange(int index) {
   DCHECK(base::Contains(visible_views_indices_, index));
   const ShelfItem& dragged_item = model_->items()[index];
 
@@ -1826,34 +1816,34 @@
                           visible_views_indices_.back());
   }
 
-  absl::optional<size_t> first = absl::nullopt;
-  absl::optional<size_t> last = absl::nullopt;
-  for (size_t i : visible_views_indices_) {
+  int first = -1;
+  int last = -1;
+  for (int i : visible_views_indices_) {
     if (SameDragType(model_->items()[i].type, dragged_item.type)) {
-      if (!first.has_value())
+      if (first == -1)
         first = i;
       last = i;
-    } else if (first.has_value()) {
+    } else if (first != -1) {
       break;
     }
   }
-  DCHECK(first.has_value());
-  DCHECK(last.has_value());
+  DCHECK_NE(first, -1);
+  DCHECK_NE(last, -1);
 
   // TODO(afakhry): Consider changing this when taking into account inactive
   // desks.
-  return std::make_pair(first.value(), last.value());
+  return std::make_pair(first, last);
 }
 
-bool ShelfView::ShouldUpdateDraggedViewPinStatus(size_t dragged_view_index) {
+bool ShelfView::ShouldUpdateDraggedViewPinStatus(int dragged_view_index) {
   if (!features::IsDragUnpinnedAppToPinEnabled())
     return false;
 
   DCHECK(base::Contains(visible_views_indices_, dragged_view_index));
   bool is_moved_item_pinned =
       IsPinnedShelfItemType(model_->items()[dragged_view_index].type);
-  if (!separator_index_.has_value()) {
-    // If there is no |separator_index_|, all the apps in shelf are expected to
+  if (separator_index_ == -1) {
+    // If |separator_index_| equals to -1, all the apps in shelf are expected to
     // have the same pinned status.
     for (auto index : visible_views_indices_) {
       if (index != dragged_view_index) {
@@ -1868,8 +1858,7 @@
   // If the separator is shown, check whether the pin status of dragged item
   // matches the pin status implied by the dragged view position relative to the
   // separator.
-  bool should_pinned_by_position =
-      dragged_view_index <= separator_index_.value();
+  bool should_pinned_by_position = dragged_view_index <= separator_index_;
   return should_pinned_by_position != is_moved_item_pinned;
 }
 
@@ -2031,8 +2020,7 @@
   return bounds;
 }
 
-absl::optional<size_t> ShelfView::CancelDrag(
-    absl::optional<size_t> modified_index) {
+int ShelfView::CancelDrag(int modified_index) {
   drag_scroll_dir_ = 0;
   scrolling_timer_.Stop();
   speed_up_drag_scrolling_.Stop();
@@ -2045,7 +2033,7 @@
   if (!drag_view_)
     return modified_index;
   bool was_dragging = dragging();
-  auto drag_view_index = view_model_->GetIndexOfView(drag_view_);
+  int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   drag_pointer_ = NONE;
   drag_view_ = nullptr;
   if (drag_view_index == modified_index) {
@@ -2057,18 +2045,16 @@
 
   // Restore previous position, tracking the position of the modified view.
   bool at_end = modified_index == view_model_->view_size();
-  views::View* modified_view =
-      (modified_index.has_value() && !at_end)
-          ? view_model_->view_at(modified_index.value())
-          : nullptr;
-  model_->Move(drag_view_index.value(), start_drag_index_.value());
+  views::View* modified_view = (modified_index >= 0 && !at_end)
+                                   ? view_model_->view_at(modified_index)
+                                   : nullptr;
+  model_->Move(drag_view_index, start_drag_index_);
 
   // If the modified view will be at the end of the list, return the new end of
   // the list.
   if (at_end)
     return view_model_->view_size();
-  return modified_view ? view_model_->GetIndexOfView(modified_view)
-                       : absl::nullopt;
+  return modified_view ? view_model_->GetIndexOfView(modified_view) : -1;
 }
 
 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
@@ -2090,9 +2076,9 @@
   {
     base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
                                           true);
-    model_index = static_cast<int>(CancelDrag(model_index).value());
+    model_index = CancelDrag(model_index);
   }
-  view_model_->Add(view, static_cast<size_t>(model_index));
+  view_model_->Add(view, model_index);
 
   // If |item| is pinned and the mutation is user-triggered, report the pinning
   // action for accessibility and UMA. Do it now, because if |item| is hidden
@@ -2122,8 +2108,7 @@
   // spot (because it was in the middle of animating from 0,0 0x0 to its
   // target).
   CalculateIdealBounds();
-  view->SetBoundsRect(
-      view_model_->ideal_bounds(static_cast<size_t>(model_index)));
+  view->SetBoundsRect(view_model_->ideal_bounds(model_index));
 
   if (model_->is_current_mutation_user_triggered() &&
       drag_and_drop_shelf_id_ != item.id) {
@@ -2134,7 +2119,7 @@
   // is hidden, so it visually appears as though we are providing space for
   // it. When done we'll fade the view in.
   AnimateToIdealBounds();
-  DCHECK_LE(static_cast<size_t>(model_index), visible_views_indices_.back());
+  DCHECK_LE(model_index, visible_views_indices_.back());
   bounds_animator_->SetAnimationDelegate(
       view, std::unique_ptr<gfx::AnimationDelegate>(
                 new StartFadeAnimationDelegate(this, view)));
@@ -2162,7 +2147,7 @@
   {
     base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
                                           true);
-    CancelDrag(absl::nullopt);
+    CancelDrag(-1);
   }
 
   if (view.get() == shelf_->tooltip()->GetCurrentAnchorView())
@@ -2214,7 +2199,7 @@
   // Bail if the view and shelf sizes do not match. ShelfItemChanged may be
   // called here before ShelfItemAdded, due to ChromeShelfController's
   // item initialization, which calls SetItem during ShelfItemAdded.
-  if (model_->items().size() != view_model_->view_size())
+  if (static_cast<int>(model_->items().size()) != view_model_->view_size())
     return;
 
   const ShelfItem& item = model_->items()[model_index];
@@ -2230,7 +2215,7 @@
 
   if (old_item.type != item.type) {
     // Type changed, swap the views.
-    model_index = static_cast<int>(CancelDrag(model_index).value());
+    model_index = CancelDrag(model_index);
     std::unique_ptr<views::View> old_view(view_model_->view_at(model_index));
     bounds_animator_->StopAnimatingView(old_view.get());
     // Removing and re-inserting a view in our view model will strip the ideal
@@ -2368,7 +2353,7 @@
 void ShelfView::OnShelfAlignmentChanged(aura::Window* root_window,
                                         ShelfAlignment old_alignment) {
   LayoutToIdealBounds();
-  for (size_t visible_index : visible_views_indices_)
+  for (const auto& visible_index : visible_views_indices_)
     view_model_->view_at(visible_index)->Layout();
 
   AnnounceShelfAlignment();
@@ -2554,9 +2539,8 @@
 }
 
 const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
-  const auto view_index = view_model_->GetIndexOfView(view);
-  return (!view_index.has_value()) ? nullptr
-                                   : &(model_->items()[view_index.value()]);
+  const int view_index = view_model_->GetIndexOfView(view);
+  return (view_index < 0) ? nullptr : &(model_->items()[view_index]);
 }
 
 int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
@@ -2602,7 +2586,7 @@
 
 void ShelfView::UpdateShelfItemViewsVisibility() {
   visible_views_indices_.clear();
-  for (size_t i = 0; i < view_model_->view_size(); ++i) {
+  for (int i = 0; i < view_model_->view_size(); ++i) {
     View* view = view_model_->view_at(i);
     // To receive drag event continuously from |drag_view_| during the dragging
     // off from the shelf, don't make |drag_view_| invisible. It will be
@@ -2650,7 +2634,7 @@
 
 void ShelfView::RemoveGhostView() {
   if (current_ghost_view_) {
-    current_ghost_view_index_ = absl::nullopt;
+    current_ghost_view_index_ = -1;
     current_ghost_view_->FadeOut();
     current_ghost_view_ = nullptr;
   }
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 1e1f210..a9bfd4b 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -305,12 +305,10 @@
 
   ShelfAppButton* drag_view() { return drag_view_; }
 
-  const std::vector<size_t>& visible_views_indices() const {
+  const std::vector<int>& visible_views_indices() const {
     return visible_views_indices_;
   }
-  size_t number_of_visible_apps() const {
-    return visible_views_indices_.size();
-  }
+  int number_of_visible_apps() const { return visible_views_indices_.size(); }
   ShelfWidget* shelf_widget() const { return shelf_->shelf_widget(); }
   const views::ViewModel* view_model() const { return view_model_.get(); }
   ShelfID drag_and_drop_shelf_id() const { return drag_and_drop_shelf_id_; }
@@ -324,9 +322,7 @@
     return shelf_menu_model_adapter_.get();
   }
 
-  absl::optional<size_t> current_ghost_view_index() const {
-    return current_ghost_view_index_;
-  }
+  int current_ghost_view_index() const { return current_ghost_view_index_; }
 
  private:
   friend class ShelfViewTestAPI;
@@ -425,11 +421,11 @@
 
   // Returns the range (in the model) the item at the specified index can be
   // dragged to.
-  std::pair<size_t, size_t> GetDragRange(size_t index);
+  std::pair<int, int> GetDragRange(int index);
 
   // Checks if the item at |dragged_item_index| should be pinned or unpinned on
   // pointer release.
-  bool ShouldUpdateDraggedViewPinStatus(size_t dragged_item_index);
+  bool ShouldUpdateDraggedViewPinStatus(int dragged_item_index);
 
   // Checks if |dragged_view| is allowed to be dragged across the separator to
   // perform pinning and unpinning. Note that this function doesn't check if the
@@ -438,7 +434,7 @@
 
   // If there is a drag operation in progress it's canceled. If |modified_index|
   // is valid, the new position of the corresponding item is returned.
-  absl::optional<size_t> CancelDrag(absl::optional<size_t> modified_index);
+  int CancelDrag(int modified_index);
 
   // Returns rectangle bounds used for drag insertion.
   gfx::Rect GetBoundsForDragInsertInScreen();
@@ -573,7 +569,7 @@
   std::unique_ptr<views::ViewModel> view_model_;
 
   // The indices of the views in |view_model_| that are visible.
-  std::vector<size_t> visible_views_indices_;
+  std::vector<int> visible_views_indices_;
 
   std::unique_ptr<views::BoundsAnimator> bounds_animator_;
 
@@ -593,8 +589,8 @@
   // items.
   views::Separator* separator_ = nullptr;
 
-  // Index of |separator_|. It is set to nullopt if it is invisible.
-  absl::optional<size_t> separator_index_ = absl::nullopt;
+  // Index of |separator_|. It is set to -1 if it is invisible.
+  int separator_index_ = -1;
 
   // Used in |drag_view_relative_to_ideal_bounds_| to represent the relative
   // position between |drag_view_| and its ideal bounds in shelf.
@@ -616,7 +612,7 @@
   gfx::Point drag_origin_;
 
   // Index |drag_view_| was initially at.
-  absl::optional<size_t> start_drag_index_ = absl::nullopt;
+  int start_drag_index_ = -1;
 
   // Used for the context menu of a particular item.
   ShelfID context_menu_id_;
@@ -675,7 +671,7 @@
   // Record the index for the last pressed shelf item. This variable is used to
   // check if a repost event occurs on the same shelf item as previous one. If
   // so, the repost event should be ignored.
-  absl::optional<size_t> last_pressed_index_ = absl::nullopt;
+  int last_pressed_index_ = -1;
 
   // Tracks UMA metrics based on shelf button press actions.
   ShelfButtonPressedMetricTracker shelf_button_pressed_metric_tracker_;
@@ -724,7 +720,7 @@
   GhostImageView* last_ghost_view_ = nullptr;
 
   // The index in the shelf app icons where the |current_ghost_view_| will show.
-  absl::optional<size_t> current_ghost_view_index_ = absl::nullopt;
+  int current_ghost_view_index_ = -1;
 
   // When the scrollable shelf is enabled, |shelf_button_delegate_| should
   // be ScrollableShelfView.
diff --git a/ash/shelf/shelf_view_test_api.cc b/ash/shelf/shelf_view_test_api.cc
index 72ee4b3..a8be137 100644
--- a/ash/shelf/shelf_view_test_api.cc
+++ b/ash/shelf/shelf_view_test_api.cc
@@ -47,7 +47,7 @@
 
 ShelfViewTestAPI::~ShelfViewTestAPI() = default;
 
-size_t ShelfViewTestAPI::GetButtonCount() {
+int ShelfViewTestAPI::GetButtonCount() {
   return shelf_view_->view_model_->view_size();
 }
 
@@ -159,7 +159,7 @@
   shelf_view_->context_menu_shown_callback_ = std::move(closure);
 }
 
-absl::optional<size_t> ShelfViewTestAPI::GetSeparatorIndex() const {
+int ShelfViewTestAPI::GetSeparatorIndex() const {
   return shelf_view_->separator_index_;
 }
 
diff --git a/ash/shelf/shelf_view_test_api.h b/ash/shelf/shelf_view_test_api.h
index 02354667..5420ac3 100644
--- a/ash/shelf/shelf_view_test_api.h
+++ b/ash/shelf/shelf_view_test_api.h
@@ -7,7 +7,6 @@
 
 #include "ash/public/cpp/shelf_item.h"
 #include "base/callback_forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/ui_base_types.h"
 
 namespace base {
@@ -42,7 +41,7 @@
   ~ShelfViewTestAPI();
 
   // Number of icons displayed.
-  size_t GetButtonCount();
+  int GetButtonCount();
 
   // Retrieve the button at |index|, doesn't support the home button,
   // because the home button is not a ShelfAppButton.
@@ -123,7 +122,7 @@
   void SetShelfContextMenuCallback(base::RepeatingClosure closure);
 
   // Returns |separator_index_|.
-  absl::optional<size_t> GetSeparatorIndex() const;
+  int GetSeparatorIndex() const;
 
   // Checks whether the separator is visible or not.
   bool IsSeparatorVisible() const;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index b1a7895..8fe3a1c 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -667,16 +667,14 @@
   AddAppShortcut();
   const auto app_id = AddApp();
 
-  EXPECT_EQ(static_cast<size_t>(model_->item_count()),
-            shelf_view_->number_of_visible_apps());
+  EXPECT_EQ(model_->item_count(), shelf_view_->number_of_visible_apps());
   const gfx::Rect visible_items_bounds =
       test_api_->visible_shelf_item_bounds_union();
 
   // Pin the app with `app_id` and expect that the visible items bounds union
   // remains the same.
   SetShelfItemTypeToAppShortcut(app_id);
-  EXPECT_EQ(static_cast<size_t>(model_->item_count()),
-            shelf_view_->number_of_visible_apps());
+  EXPECT_EQ(model_->item_count(), shelf_view_->number_of_visible_apps());
   EXPECT_EQ(visible_items_bounds, test_api_->visible_shelf_item_bounds_union());
 }
 
@@ -805,7 +803,7 @@
 TEST_F(ShelfViewDragToPinTest, DragAppsToPinAndUnpin) {
   std::vector<std::pair<ShelfID, views::View*>> id_map;
   SetupForDragTest(&id_map);
-  size_t pinned_apps_size = id_map.size();
+  int pinned_apps_size = id_map.size();
 
   const ShelfID open_app_id = AddApp();
   id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
@@ -871,7 +869,7 @@
 TEST_F(ShelfViewDragToPinTest, BlockBrowserShortcutFromUnpinningByDragging) {
   std::vector<std::pair<ShelfID, views::View*>> id_map;
   SetupForDragTest(&id_map);
-  const size_t pinned_apps_size = id_map.size();
+  const int pinned_apps_size = id_map.size();
 
   const ShelfID open_app_id = AddApp();
   id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
@@ -900,7 +898,7 @@
 TEST_F(ShelfViewDragToPinTest, DragAppAroundSeparator) {
   std::vector<std::pair<ShelfID, views::View*>> id_map;
   SetupForDragTest(&id_map);
-  const size_t pinned_apps_size = id_map.size();
+  const int pinned_apps_size = id_map.size();
 
   const ShelfID open_app_id = AddApp();
   id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
@@ -915,8 +913,8 @@
 
   // Drag an unpinned open app that is beside the separator around and check
   // that the separator is correctly placed.
-  ASSERT_EQ(static_cast<size_t>(model_->ItemIndexByID(open_app_id)),
-            test_api_->GetSeparatorIndex().value() + 1);
+  ASSERT_EQ(model_->ItemIndexByID(open_app_id),
+            test_api_->GetSeparatorIndex() + 1);
   gfx::Point unpinned_app_location =
       GetButtonCenter(GetButtonByID(open_app_id));
   generator->set_current_screen_location(unpinned_app_location);
@@ -926,9 +924,9 @@
   generator->MoveMouseBy(-button_width / 4, 0);
   EXPECT_EQ(1, GetHapticTickEventsCount());
   // In this case, the separator is moved to the end of the shelf so it is set
-  // invisible and the |separator_index_| will be updated to nullopt.
+  // invisible and the |separator_index_| will be updated to -1.
   EXPECT_FALSE(test_api_->IsSeparatorVisible());
-  EXPECT_FALSE(test_api_->GetSeparatorIndex().has_value());
+  EXPECT_EQ(test_api_->GetSeparatorIndex(), -1);
   // Drag the mouse slightly to the right where the dragged app will stay at the
   // same index.
   generator->MoveMouseBy(button_width / 2, 0);
@@ -942,8 +940,7 @@
   // separator is correctly placed. Check that the dragged app is not a browser
   // shortcut, which can not be dragged across the separator.
   ASSERT_NE(model_->items()[pinned_apps_size - 1].type, TYPE_BROWSER_SHORTCUT);
-  ASSERT_EQ(static_cast<size_t>(
-                model_->ItemIndexByID(id_map[pinned_apps_size - 1].first)),
+  ASSERT_EQ(model_->ItemIndexByID(id_map[pinned_apps_size - 1].first),
             test_api_->GetSeparatorIndex());
   gfx::Point pinned_app_location =
       GetButtonCenter(id_map[pinned_apps_size - 1].first);
@@ -1386,7 +1383,7 @@
       << "We should not be showing the app list";
 
   // The tooltip shouldn't hide if the mouse is on normal buttons.
-  for (size_t i = 0; i < test_api_->GetButtonCount(); i++) {
+  for (int i = 0; i < test_api_->GetButtonCount(); i++) {
     ShelfAppButton* button = test_api_->GetButton(i);
     if (!button)
       continue;
@@ -1400,7 +1397,7 @@
   const int left = home_button->GetBoundsInScreen().right();
   // Find the first shelf button that's to the right of the home button.
   int right = 0;
-  for (size_t i = 0; i < test_api_->GetButtonCount(); ++i) {
+  for (int i = 0; i < test_api_->GetButtonCount(); ++i) {
     ShelfAppButton* button = test_api_->GetButton(i);
     if (!button)
       continue;
@@ -1426,7 +1423,7 @@
 
   // The tooltip should hide if it's outside of all buttons.
   gfx::Rect all_area;
-  for (size_t i = 0; i < test_api_->GetButtonCount(); i++) {
+  for (int i = 0; i < test_api_->GetButtonCount(); i++) {
     ShelfAppButton* button = test_api_->GetButton(i);
     if (!button)
       continue;
@@ -1450,7 +1447,7 @@
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
 
   // The tooltip shouldn't hide if the mouse is on normal buttons.
-  for (size_t i = 2; i < test_api_->GetButtonCount(); i++) {
+  for (int i = 2; i < test_api_->GetButtonCount(); i++) {
     ShelfAppButton* button = test_api_->GetButton(i);
     if (!button)
       continue;
@@ -2229,16 +2226,16 @@
   // At the start, the only visible app on the shelf is the browser app button
   // (index 0).
   ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
-  EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
+  EXPECT_EQ(0, shelf_view_->visible_views_indices()[0]);
   // By enabling tablet mode, the back button (index 0) should become visible,
   // but that does not change the first and last visible indices.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
-  EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
+  EXPECT_EQ(0, shelf_view_->visible_views_indices()[0]);
   // Turn tablet mode off again.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
   ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
-  EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
+  EXPECT_EQ(0, shelf_view_->visible_views_indices()[0]);
 }
 
 TEST_P(LtrRtlShelfViewTest, ReplacingDelegateCancelsContextMenu) {
@@ -2347,7 +2344,7 @@
   // Start shelf party.
   model_->ToggleShelfParty();
   {
-    const std::vector<size_t> not_partying = {1, 3};
+    const std::vector<int> not_partying = {1, 3};
     EXPECT_EQ(not_partying, shelf_view_->visible_views_indices());
   }
   test_api_->RunMessageLoopUntilAnimationsDone();
@@ -2357,7 +2354,7 @@
   // End shelf party.
   model_->ToggleShelfParty();
   {
-    const std::vector<size_t> not_partying = {0, 1, 2, 3};
+    const std::vector<int> not_partying = {0, 1, 2, 3};
     EXPECT_EQ(not_partying, shelf_view_->visible_views_indices());
   }
   test_api_->RunMessageLoopUntilAnimationsDone();
@@ -2460,21 +2457,21 @@
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 
   ShelfID second_app_id = id_map[1].first;
   GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
+  EXPECT_EQ(1, shelf_view_->current_ghost_view_index());
 
   GetEventGenerator()->ReleaseTouch();
   EXPECT_EQ(0, GetHapticTickEventsCount());
 
   EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 }
 
 // Tests that the ghost image is removed if the app is dragged outide of the
@@ -2489,14 +2486,14 @@
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 
   ShelfID second_app_id = id_map[1].first;
   GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
+  EXPECT_EQ(1, shelf_view_->current_ghost_view_index());
 
   // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
   constexpr int kRipOffDistance = 48;
@@ -2506,14 +2503,14 @@
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 
   GetEventGenerator()->ReleaseTouch();
   EXPECT_EQ(0, GetHapticTickEventsCount());
 
   EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 }
 
 // Disabled as likely to cause builder failure, see  https://crbug.com/1334936.
@@ -2529,14 +2526,14 @@
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 
   ShelfID second_app_id = id_map[1].first;
   GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
+  EXPECT_EQ(1, shelf_view_->current_ghost_view_index());
 
   // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
   constexpr int kRipOffDistance = 48;
@@ -2546,20 +2543,20 @@
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 
   GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
 
   EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_TRUE(shelf_view_->drag_view());
-  EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
+  EXPECT_EQ(1, shelf_view_->current_ghost_view_index());
 
   GetEventGenerator()->ReleaseTouch();
   EXPECT_EQ(0, GetHapticTickEventsCount());
 
   EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
   EXPECT_FALSE(shelf_view_->drag_view());
-  EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
+  EXPECT_EQ(-1, shelf_view_->current_ghost_view_index());
 }
 
 class ShelfViewVisibleBoundsTest : public ShelfViewTest,
@@ -2575,7 +2572,7 @@
     gfx::Rect visible_bounds = shelf_view_->GetVisibleItemsBoundsInScreen();
     gfx::Rect shelf_bounds = shelf_view_->GetBoundsInScreen();
     EXPECT_TRUE(shelf_bounds.Contains(visible_bounds));
-    for (size_t i = 0; i < test_api_->GetButtonCount(); ++i)
+    for (int i = 0; i < test_api_->GetButtonCount(); ++i)
       if (ShelfAppButton* button = test_api_->GetButton(i)) {
         if (button->GetVisible())
           EXPECT_TRUE(visible_bounds.Contains(button->GetBoundsInScreen()));
@@ -3230,7 +3227,7 @@
   // There are five buttons, including 3 app buttons. The back button and
   // launcher are always there, the browser shortcut is added in
   // ShelfViewTest and the two test apps added in ShelfViewFocusTest.
-  EXPECT_EQ(3u, test_api_->GetButtonCount());
+  EXPECT_EQ(3, test_api_->GetButtonCount());
   EXPECT_TRUE(GetPrimaryShelf()->navigation_widget()->IsActive());
 
   // The home button is focused initially because the back button is only
diff --git a/ash/system/phonehub/camera_roll_view.cc b/ash/system/phonehub/camera_roll_view.cc
index 468cac3..3ce8ef0 100644
--- a/ash/system/phonehub/camera_roll_view.cc
+++ b/ash/system/phonehub/camera_roll_view.cc
@@ -108,7 +108,7 @@
 
 void CameraRollView::CameraRollItemsView::AddCameraRollItem(
     views::View* camera_roll_item) {
-  size_t view_size = camera_roll_items_.view_size();
+  int view_size = camera_roll_items_.view_size();
   camera_roll_items_.Add(camera_roll_item, view_size);
   AddChildView(camera_roll_item);
 }
@@ -135,7 +135,7 @@
 void CameraRollView::CameraRollItemsView::Layout() {
   views::View::Layout();
   CalculateIdealBounds();
-  for (size_t i = 0; i < camera_roll_items_.view_size(); ++i) {
+  for (int i = 0; i < camera_roll_items_.view_size(); ++i) {
     auto* thumbnail = camera_roll_items_.view_at(i);
     thumbnail->SetBoundsRect(camera_roll_items_.ideal_bounds(i));
   }
@@ -158,7 +158,7 @@
 }
 
 void CameraRollView::CameraRollItemsView::CalculateIdealBounds() {
-  for (size_t i = 0; i < camera_roll_items_.view_size(); ++i) {
+  for (int i = 0; i < camera_roll_items_.view_size(); ++i) {
     gfx::Rect camera_roll_item_bounds =
         gfx::Rect(GetCameraRollItemPosition(i), GetCameraRollItemSize());
     camera_roll_items_.set_ideal_bounds(i, camera_roll_item_bounds);
diff --git a/ash/system/phonehub/task_continuation_view.cc b/ash/system/phonehub/task_continuation_view.cc
index 2df9381..9c0c967e 100644
--- a/ash/system/phonehub/task_continuation_view.cc
+++ b/ash/system/phonehub/task_continuation_view.cc
@@ -104,7 +104,7 @@
 TaskContinuationView::TaskChipsView::~TaskChipsView() = default;
 
 void TaskContinuationView::TaskChipsView::AddTaskChip(views::View* task_chip) {
-  size_t view_size = task_chips_.view_size();
+  int view_size = task_chips_.view_size();
   task_chips_.Add(task_chip, view_size);
   AddChildView(task_chip);
 }
@@ -127,7 +127,7 @@
 void TaskContinuationView::TaskChipsView::Layout() {
   views::View::Layout();
   CalculateIdealBounds();
-  for (size_t i = 0; i < task_chips_.view_size(); ++i) {
+  for (int i = 0; i < task_chips_.view_size(); ++i) {
     auto* button = task_chips_.view_at(i);
     button->SetBoundsRect(task_chips_.ideal_bounds(i));
   }
@@ -154,7 +154,7 @@
 }
 
 void TaskContinuationView::TaskChipsView::CalculateIdealBounds() {
-  for (size_t i = 0; i < task_chips_.view_size(); ++i) {
+  for (int i = 0; i < task_chips_.view_size(); ++i) {
     gfx::Rect tile_bounds =
         gfx::Rect(GetButtonPosition(i), GetTaskContinuationChipSize());
     task_chips_.set_ideal_bounds(i, tile_bounds);
diff --git a/ash/system/unified/feature_pods_container_view.cc b/ash/system/unified/feature_pods_container_view.cc
index e800760..9b932372 100644
--- a/ash/system/unified/feature_pods_container_view.cc
+++ b/ash/system/unified/feature_pods_container_view.cc
@@ -116,7 +116,7 @@
 void FeaturePodsContainerView::Layout() {
   UpdateCollapsedSidePadding();
   CalculateIdealBoundsForFeaturePods();
-  for (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
+  for (int i = 0; i < visible_buttons_.view_size(); ++i) {
     auto* button = visible_buttons_.view_at(i);
     button->SetBoundsRect(visible_buttons_.ideal_bounds(i));
   }
@@ -136,14 +136,12 @@
     bool visible = IsButtonVisible(child, visible_count);
     child->SetVisibleByContainer(visible);
     if (visible) {
-      if (!visible_buttons_.GetIndexOfView(child).has_value())
+      if (visible_buttons_.GetIndexOfView(child) < 0)
         visible_buttons_.Add(child, visible_count);
       ++visible_count;
     } else {
-      if (auto index = visible_buttons_.GetIndexOfView(child);
-          index.has_value()) {
-        visible_buttons_.Remove(index.value());
-      }
+      if (visible_buttons_.GetIndexOfView(child))
+        visible_buttons_.Remove(visible_buttons_.GetIndexOfView(child));
     }
   }
   UpdateTotalPages();
@@ -165,17 +163,17 @@
 }
 
 void FeaturePodsContainerView::EnsurePageWithButton(views::View* button) {
-  auto index = visible_buttons_.GetIndexOfView(button->parent());
-  if (!index.has_value())
+  int index = visible_buttons_.GetIndexOfView(button->parent());
+  if (index < 0)
     return;
 
   int tiles_per_page = GetTilesPerPage();
-  size_t first_index = pagination_model_->selected_page() * tiles_per_page;
-  size_t last_index =
+  int first_index = pagination_model_->selected_page() * tiles_per_page;
+  int last_index =
       ((pagination_model_->selected_page() + 1) * tiles_per_page) - 1;
-  if (index.value() < first_index || index.value() > last_index) {
-    int page = ((index.value() + 1) / tiles_per_page) +
-               ((index.value() + 1) % tiles_per_page ? 1 : 0) - 1;
+  if (index < first_index || index > last_index) {
+    int page = ((index + 1) / tiles_per_page) +
+               ((index + 1) % tiles_per_page ? 1 : 0) - 1;
 
     pagination_model_->SelectPage(page, true /*animate*/);
   }
@@ -275,7 +273,7 @@
 }
 
 void FeaturePodsContainerView::AddFeaturePodButton(FeaturePodButton* button) {
-  size_t view_size = visible_buttons_.view_size();
+  int view_size = visible_buttons_.view_size();
   if (IsButtonVisible(button, view_size)) {
     visible_buttons_.Add(button, view_size);
   }
@@ -320,7 +318,7 @@
 }
 
 void FeaturePodsContainerView::CalculateIdealBoundsForFeaturePods() {
-  for (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
+  for (int i = 0; i < visible_buttons_.view_size(); ++i) {
     gfx::Rect tile_bounds;
     gfx::Size child_size;
     // When we are on the first page we calculate bounds for an expanded tray
@@ -360,7 +358,7 @@
 void FeaturePodsContainerView::UpdateTotalPages() {
   int total_pages = 0;
 
-  size_t total_visible = visible_buttons_.view_size();
+  int total_visible = visible_buttons_.view_size();
   int tiles_per_page = GetTilesPerPage();
 
   if (!visible_buttons_.view_size() || !tiles_per_page) {
diff --git a/ash/webui/camera_app_ui/camera_app_helper.mojom b/ash/webui/camera_app_ui/camera_app_helper.mojom
index 017ac73..a8f385a7 100644
--- a/ash/webui/camera_app_ui/camera_app_helper.mojom
+++ b/ash/webui/camera_app_ui/camera_app_helper.mojom
@@ -25,6 +25,16 @@
     OFF_AUTO,
 };
 
+// Ready state for document scanner.
+enum DocumentScannerReadyState {
+  // The library is not supported on the device.
+  NOT_SUPPORTED,
+  // The library is supported but needs to wait for its installation.
+  SUPPORTED_BUT_NOT_READY,
+  // The library is supported and ready.
+  SUPPORTED_AND_READY,
+};
+
 // Interface for monitoring screen state of device. The state is detected from
 // Chrome browser process and is notified to Chrome Camera App in renderer
 // process.
@@ -178,8 +188,13 @@
   // by result.
   MonitorFileDeletion(string name) => (FileMonitorResult result);
 
-  // Returns true if document mode is supported on the device.
-  IsDocumentModeSupported() => (bool is_supported);
+  // Returns the ready type of the document scanner on the device.
+  GetDocumentScannerReadyState() => (DocumentScannerReadyState ready_state);
+
+  // Registers a callback which will be resolved once the document scanner is
+  // ready or when it fails to load. If the document scanner is not supported on
+  // the device, it returns false immediately.
+  RegisterDocumentScannerReadyCallback() => (bool is_loaded);
 
   // Returns the detected document corners from given |jpeg_data|.
   // The amount of corners will be either 0, indicating there are no corners
diff --git a/ash/webui/camera_app_ui/camera_app_helper_impl.cc b/ash/webui/camera_app_ui/camera_app_helper_impl.cc
index 05e1ab2..8485651 100644
--- a/ash/webui/camera_app_ui/camera_app_helper_impl.cc
+++ b/ash/webui/camera_app_ui/camera_app_helper_impl.cc
@@ -288,10 +288,30 @@
                 std::move(callback)));
 }
 
-void CameraAppHelperImpl::IsDocumentModeSupported(
-    IsDocumentModeSupportedCallback callback) {
-  bool supported = document_scanner_service_ != nullptr;
-  std::move(callback).Run(supported);
+void CameraAppHelperImpl::GetDocumentScannerReadyState(
+    GetDocumentScannerReadyStateCallback callback) {
+  if (document_scanner_service_ == nullptr) {
+    std::move(callback).Run(
+        camera_app::mojom::DocumentScannerReadyState::NOT_SUPPORTED);
+    return;
+  }
+  if (document_scanner_service_->IsLoaded()) {
+    std::move(callback).Run(
+        camera_app::mojom::DocumentScannerReadyState::SUPPORTED_AND_READY);
+    return;
+  }
+  std::move(callback).Run(
+      camera_app::mojom::DocumentScannerReadyState::SUPPORTED_BUT_NOT_READY);
+}
+
+void CameraAppHelperImpl::RegisterDocumentScannerReadyCallback(
+    RegisterDocumentScannerReadyCallbackCallback callback) {
+  if (document_scanner_service_ == nullptr) {
+    std::move(callback).Run(false);
+    return;
+  }
+  document_scanner_service_->RegisterDocumentScannerReadyCallback(
+      std::move(callback));
 }
 
 void CameraAppHelperImpl::ScanDocumentCorners(
diff --git a/ash/webui/camera_app_ui/camera_app_helper_impl.h b/ash/webui/camera_app_ui/camera_app_helper_impl.h
index d8a6002b..ae52190 100644
--- a/ash/webui/camera_app_ui/camera_app_helper_impl.h
+++ b/ash/webui/camera_app_ui/camera_app_helper_impl.h
@@ -79,8 +79,10 @@
   void SendNewCaptureBroadcast(bool is_video, const std::string& name) override;
   void MonitorFileDeletion(const std::string& name,
                            MonitorFileDeletionCallback callback) override;
-  void IsDocumentModeSupported(
-      IsDocumentModeSupportedCallback callback) override;
+  void GetDocumentScannerReadyState(
+      GetDocumentScannerReadyStateCallback callback) override;
+  void RegisterDocumentScannerReadyCallback(
+      RegisterDocumentScannerReadyCallbackCallback callback) override;
   void ScanDocumentCorners(const std::vector<uint8_t>& jpeg_data,
                            ScanDocumentCornersCallback callback) override;
   void ConvertToDocument(const std::vector<uint8_t>& jpeg_data,
diff --git a/ash/webui/camera_app_ui/document_scanner_installer.cc b/ash/webui/camera_app_ui/document_scanner_installer.cc
index 1cc7d61..6c043c2 100644
--- a/ash/webui/camera_app_ui/document_scanner_installer.cc
+++ b/ash/webui/camera_app_ui/document_scanner_installer.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/camera_app_ui/document_scanner_installer.h"
 
+#include "ash/webui/camera_app_ui/document_scanner_service_client.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -36,6 +37,10 @@
 void DocumentScannerInstaller::TriggerInstall() {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
 
+  if (!DocumentScannerServiceClient::IsSupportedByDlc()) {
+    return;
+  }
+
   dlcservice::InstallRequest install_request;
   install_request.set_id(kDocumentScannerDlcId);
   chromeos::DlcserviceClient::Get()->Install(
diff --git a/ash/webui/camera_app_ui/document_scanner_service_client.cc b/ash/webui/camera_app_ui/document_scanner_service_client.cc
index 4df61ccd..1fc962e 100644
--- a/ash/webui/camera_app_ui/document_scanner_service_client.cc
+++ b/ash/webui/camera_app_ui/document_scanner_service_client.cc
@@ -4,8 +4,11 @@
 
 #include "ash/webui/camera_app_ui/document_scanner_service_client.h"
 
+#include "ash/webui/camera_app_ui/document_scanner_installer.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/task/bind_post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/device_event_log/device_event_log.h"
 
@@ -21,6 +24,8 @@
 
 constexpr char kOndeviceDocumentScanner[] = "ondevice_document_scanner";
 constexpr char kMLService[] = "ml_service";
+constexpr char kLibDocumentScannerDefaultDir[] =
+    "/usr/share/cros-camera/libfs/";
 
 // Returns whether the `value` is set for command line switch
 // kOndeviceDocumentScanner.
@@ -35,6 +40,11 @@
   return HasCommandLineSwitch(kOndeviceDocumentScanner, "use_rootfs");
 }
 
+// Returns true if switch kOndeviceDocumentScanner is set to use_dlc.
+bool IsEnabledOnDlc() {
+  return HasCommandLineSwitch(kOndeviceDocumentScanner, "use_dlc");
+}
+
 bool IsMachineLearningServiceAvailable() {
   return HasCommandLineSwitch(kMLService, "enabled");
 }
@@ -43,7 +53,13 @@
 
 // static
 bool DocumentScannerServiceClient::IsSupported() {
-  return IsMachineLearningServiceAvailable() && IsEnabledOnRootfs();
+  return IsMachineLearningServiceAvailable() &&
+         (IsEnabledOnRootfs() || IsEnabledOnDlc());
+}
+
+// static
+bool DocumentScannerServiceClient::IsSupportedByDlc() {
+  return IsMachineLearningServiceAvailable() && IsEnabledOnDlc();
 }
 
 // static
@@ -58,15 +74,24 @@
 
 DocumentScannerServiceClient::~DocumentScannerServiceClient() = default;
 
+void DocumentScannerServiceClient::RegisterDocumentScannerReadyCallback(
+    OnReadyCallback callback) {
+  base::AutoLock auto_lock(load_status_lock_);
+  if (document_scanner_loaded_) {
+    std::move(callback).Run(true);
+    return;
+  }
+  on_ready_callbacks_.push_back(std::move(callback));
+}
+
 bool DocumentScannerServiceClient::IsLoaded() {
+  base::AutoLock auto_lock(load_status_lock_);
   return document_scanner_loaded_;
 }
 
 void DocumentScannerServiceClient::DetectCornersFromNV12Image(
     base::ReadOnlySharedMemoryRegion nv12_image,
     DetectCornersCallback callback) {
-  DCHECK(IsSupported());
-
   if (!IsLoaded()) {
     std::move(callback).Run(false, {});
     return;
@@ -86,8 +111,6 @@
 void DocumentScannerServiceClient::DetectCornersFromJPEGImage(
     base::ReadOnlySharedMemoryRegion jpeg_image,
     DetectCornersCallback callback) {
-  DCHECK(IsSupported());
-
   if (!IsLoaded()) {
     std::move(callback).Run(false, {});
     return;
@@ -109,8 +132,6 @@
     const std::vector<gfx::PointF>& corners,
     Rotation rotation,
     DoPostProcessingCallback callback) {
-  DCHECK(IsSupported());
-
   if (!IsLoaded()) {
     std::move(callback).Run(false, {});
     return;
@@ -131,15 +152,35 @@
 DocumentScannerServiceClient::DocumentScannerServiceClient() {
   chromeos::machine_learning::ServiceConnection::GetInstance()
       ->BindMachineLearningService(ml_service_.BindNewPipeAndPassReceiver());
+  if (IsEnabledOnRootfs()) {
+    LoadDocumentScanner(kLibDocumentScannerDefaultDir);
+  } else if (IsEnabledOnDlc()) {
+    DocumentScannerInstaller::GetInstance()->GetLibraryPath(base::BindPostTask(
+        base::SequencedTaskRunnerHandle::Get(),
+        base::BindOnce(&DocumentScannerServiceClient::LoadDocumentScanner,
+                       weak_ptr_factory_.GetWeakPtr())));
+  }
+}
+
+void DocumentScannerServiceClient::LoadDocumentScanner(
+    const std::string& lib_path) {
+  auto config = chromeos::machine_learning::mojom::DocumentScannerConfig::New();
+  config->library_dlc_path = base::FilePath(lib_path);
+
   ml_service_->LoadDocumentScanner(
-      document_scanner_.BindNewPipeAndPassReceiver(),
-      base::BindOnce(&DocumentScannerServiceClient::OnInitialized,
+      document_scanner_.BindNewPipeAndPassReceiver(), std::move(config),
+      base::BindOnce(&DocumentScannerServiceClient::OnLoadedDocumentScanner,
                      base::Unretained(this)));
 }
 
-void DocumentScannerServiceClient::OnInitialized(
+void DocumentScannerServiceClient::OnLoadedDocumentScanner(
     chromeos::machine_learning::mojom::LoadModelResult result) {
+  base::AutoLock auto_lock(load_status_lock_);
   document_scanner_loaded_ = result == LoadModelResult::OK;
+  for (auto& callback : on_ready_callbacks_) {
+    std::move(callback).Run(document_scanner_loaded_);
+  }
+  on_ready_callbacks_.clear();
 }
 
 }  // namespace ash
diff --git a/ash/webui/camera_app_ui/document_scanner_service_client.h b/ash/webui/camera_app_ui/document_scanner_service_client.h
index 29a892a..3a4e887 100644
--- a/ash/webui/camera_app_ui/document_scanner_service_client.h
+++ b/ash/webui/camera_app_ui/document_scanner_service_client.h
@@ -6,10 +6,14 @@
 #define ASH_WEBUI_CAMERA_APP_UI_DOCUMENT_SCANNER_SERVICE_CLIENT_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
 #include "base/task/sequenced_task_runner.h"
+#include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "chromeos/services/machine_learning/public/mojom/document_scanner.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -20,6 +24,7 @@
 // Client for communicating to the CrOS Document Scanner Service.
 class DocumentScannerServiceClient {
  public:
+  using OnReadyCallback = base::OnceCallback<void(bool is_supported)>;
   using DetectCornersCallback =
       base::OnceCallback<void(bool success,
                               const std::vector<gfx::PointF>& results)>;
@@ -28,10 +33,14 @@
 
   static bool IsSupported();
 
+  static bool IsSupportedByDlc();
+
   static std::unique_ptr<DocumentScannerServiceClient> Create();
 
   ~DocumentScannerServiceClient();
 
+  void RegisterDocumentScannerReadyCallback(OnReadyCallback callback);
+
   bool IsLoaded();
 
   void DetectCornersFromNV12Image(base::ReadOnlySharedMemoryRegion nv12_image,
@@ -49,15 +58,27 @@
   DocumentScannerServiceClient();
 
  private:
-  void OnInitialized(chromeos::machine_learning::mojom::LoadModelResult result);
+  void LoadDocumentScanner(const std::string& lib_path);
 
-  bool document_scanner_loaded_ = false;
+  void OnLoadedDocumentScanner(
+      chromeos::machine_learning::mojom::LoadModelResult result);
+
+  // Guards |document_scanner_loaded_| and |on_ready_callbacks_| which are
+  // related to the load status.
+  base::Lock load_status_lock_;
+
+  bool document_scanner_loaded_ GUARDED_BY(load_status_lock_) = false;
+
+  std::vector<OnReadyCallback> on_ready_callbacks_
+      GUARDED_BY(load_status_lock_);
 
   mojo::Remote<chromeos::machine_learning::mojom::MachineLearningService>
       ml_service_;
 
   mojo::Remote<chromeos::machine_learning::mojom::DocumentScanner>
       document_scanner_;
+
+  base::WeakPtrFactory<DocumentScannerServiceClient> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/webui/camera_app_ui/resources.h b/ash/webui/camera_app_ui/resources.h
index bbb46c1..7f0f133 100644
--- a/ash/webui/camera_app_ui/resources.h
+++ b/ash/webui/camera_app_ui/resources.h
@@ -48,6 +48,8 @@
     {"document_mode_dialog_got_it", IDS_DOCUMENT_MODE_DIALOG_GOT_IT},
     {"document_mode_dialog_msg", IDS_DOCUMENT_MODE_DIALOG_MSG},
     {"document_mode_dialog_intro_title", IDS_DOCUMENT_MODE_DIALOG_INTRO_TITLE},
+    {"downloading_document_scanning_feature",
+     IDS_DOWNLOADING_DOCUMENT_SCANNING_FEATURE},
     {"error_msg_camera_paused", IDS_ERROR_MSG_CAMERA_PAUSED},
     {"error_msg_empty_recording", IDS_ERROR_MSG_EMPTY_RECORDING},
     {"error_msg_file_system_failed", IDS_ERROR_MSG_FILE_SYSTEM_FAILED},
diff --git a/ash/webui/camera_app_ui/resources/css/mode/scan.css b/ash/webui/camera_app_ui/resources/css/mode/scan.css
index bc269ecc..629dc31 100644
--- a/ash/webui/camera_app_ui/resources/css/mode/scan.css
+++ b/ash/webui/camera_app_ui/resources/css/mode/scan.css
@@ -1,25 +1,10 @@
 /* Copyright 2021 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. */
-
-body:is(.show-scan-mode, :not(.streaming), :not(.photo)) #toggle-barcode {
+body:is(:not(.scan), :not(.streaming)) #scan-modes-group {
   display: none;
 }
 
-body:is(:not(.show-scan-mode), :not(.scan), :not(.streaming)) #scan-modes-group {
-  display: none;
-}
-
-#toggle-barcode:checked {
-  background: url(/images/barcode_toggle_on.svg) center no-repeat,
-              rgba(var(--blue-300-rgb), 0.3);
-}
-
-#toggle-barcode {
-  background-image: url(/images/barcode_toggle_off.svg);
-  border-radius: 50%;
-}
-
 /* The container of scan box for layout and positioning. */
 .barcode-scan-box {
   --border-distance: 8px;
diff --git a/ash/webui/camera_app_ui/resources/images/barcode_toggle_off.svg b/ash/webui/camera_app_ui/resources/images/barcode_toggle_off.svg
deleted file mode 100644
index 59feeaa..0000000
--- a/ash/webui/camera_app_ui/resources/images/barcode_toggle_off.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" jetway-hooks="{}">
-    <title>ic_QR code</title>
-    <g jetway-hook-id="08335F75-6103-408B-A1E8-5C734C95C86B" id="ic_QR-code" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g jetway-hook-id="0922F802-CA75-47A8-91C7-3DDA7435639F" id="Scanner" transform="translate(2.000000, 2.000000)" fill="#E8EAED">
-            <path d="M0,11 L2,11 L2,14 L5,14 L5,16 L2,16 C0.8954305,16 0,15.1045695 0,14 L0,11 Z M16,11 L16,14 C16,15.1045695 15.1045695,16 14,16 L11,16 L11,14 L14,14 L14,11 L16,11 Z M14,0 C15.1045695,0 16,0.8954305 16,2 L16,5 L14,4.999 L14,2 L11,2 L11,0 L14,0 Z M5,2 L2,2 L2,5 L0,5 L0,2 C0,0.8954305 0.8954305,0 2,0 L5,0 L5,2 Z" jetway-hook-id="65066AE9-BA95-4BD2-830F-702A2BAF2373" id="Combined-Shape" fill-rule="nonzero"></path>
-            <path d="M7,3 L7,7 L3,7 L3,3 L7,3 Z M5.5,4.5 L4.5,4.5 L4.5,5.5 L5.5,5.5 L5.5,4.5 Z" jetway-hook-id="7AFE2645-6416-4C0A-86F8-9937AF5F8D2D" id="Rectangle" fill-rule="nonzero"></path>
-            <path d="M7,9 L7,13 L3,13 L3,9 L7,9 Z M5.5,10.5 L4.5,10.5 L4.5,11.5 L5.5,11.5 L5.5,10.5 Z" jetway-hook-id="60AB4502-99DF-497C-B25D-E1FEF2949C77" id="Rectangle-Copy-2" fill-rule="nonzero"></path>
-            <path d="M13,3 L13,7 L9,7 L9,3 L13,3 Z M11.5,4.5 L10.5,4.5 L10.5,5.5 L11.5,5.5 L11.5,4.5 Z" jetway-hook-id="489581C8-570A-4400-9696-1F0B5E704108" id="Rectangle-Copy" fill-rule="nonzero"></path>
-            <path d="M10.5,8.5 L10.5,10 L9.5,10 L9.5,11.5 L10.5,11.5 L10.5,13 L9,13 L9,11.5 L8,11.5 L8,10 L9,10 L9,8.5 L10.5,8.5 Z M10.5,11.5 L10.5,10 L11.5,10 L11.5,8.5 L13,8.5 L13,10 L12,10 L12,11.5 L13,11.5 L13,13 L11.5,13 L11.5,11.5 L10.5,11.5 Z" jetway-hook-id="A007E647-A565-4BF9-A946-07BABE8CA005" id="Combined-Shape"></path>
-        </g>
-    </g>
-</svg>
diff --git a/ash/webui/camera_app_ui/resources/images/barcode_toggle_on.svg b/ash/webui/camera_app_ui/resources/images/barcode_toggle_on.svg
deleted file mode 100644
index feebda2..0000000
--- a/ash/webui/camera_app_ui/resources/images/barcode_toggle_on.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" jetway-hooks="{}">
-    <title>ic_QR code</title>
-    <g jetway-hook-id="08335F75-6103-408B-A1E8-5C734C95C86B" id="ic_QR-code" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <g jetway-hook-id="0922F802-CA75-47A8-91C7-3DDA7435639F" id="Scanner" transform="translate(2.000000, 2.000000)" fill="#8AB4F8">
-            <path d="M0,11 L2,11 L2,14 L5,14 L5,16 L2,16 C0.8954305,16 0,15.1045695 0,14 L0,11 Z M16,11 L16,14 C16,15.1045695 15.1045695,16 14,16 L11,16 L11,14 L14,14 L14,11 L16,11 Z M14,0 C15.1045695,0 16,0.8954305 16,2 L16,5 L14,4.999 L14,2 L11,2 L11,0 L14,0 Z M5,2 L2,2 L2,5 L0,5 L0,2 C0,0.8954305 0.8954305,0 2,0 L5,0 L5,2 Z" jetway-hook-id="65066AE9-BA95-4BD2-830F-702A2BAF2373" id="Combined-Shape" fill-rule="nonzero"></path>
-            <path d="M7,3 L7,7 L3,7 L3,3 L7,3 Z M5.5,4.5 L4.5,4.5 L4.5,5.5 L5.5,5.5 L5.5,4.5 Z" jetway-hook-id="7AFE2645-6416-4C0A-86F8-9937AF5F8D2D" id="Rectangle" fill-rule="nonzero"></path>
-            <path d="M7,9 L7,13 L3,13 L3,9 L7,9 Z M5.5,10.5 L4.5,10.5 L4.5,11.5 L5.5,11.5 L5.5,10.5 Z" jetway-hook-id="60AB4502-99DF-497C-B25D-E1FEF2949C77" id="Rectangle-Copy-2" fill-rule="nonzero"></path>
-            <path d="M13,3 L13,7 L9,7 L9,3 L13,3 Z M11.5,4.5 L10.5,4.5 L10.5,5.5 L11.5,5.5 L11.5,4.5 Z" jetway-hook-id="489581C8-570A-4400-9696-1F0B5E704108" id="Rectangle-Copy" fill-rule="nonzero"></path>
-            <path d="M10.5,8.5 L10.5,10 L9.5,10 L9.5,11.5 L10.5,11.5 L10.5,13 L9,13 L9,11.5 L8,11.5 L8,10 L9,10 L9,8.5 L10.5,8.5 Z M10.5,11.5 L10.5,10 L11.5,10 L11.5,8.5 L13,8.5 L13,10 L12,10 L12,11.5 L13,11.5 L13,13 L11.5,13 L11.5,11.5 L10.5,11.5 Z" jetway-hook-id="A007E647-A565-4BF9-A946-07BABE8CA005" id="Combined-Shape"></path>
-        </g>
-    </g>
-</svg>
diff --git a/ash/webui/camera_app_ui/resources/images/images.gni b/ash/webui/camera_app_ui/resources/images/images.gni
index c57b873..43aeda07 100644
--- a/ash/webui/camera_app_ui/resources/images/images.gni
+++ b/ash/webui/camera_app_ui/resources/images/images.gni
@@ -7,8 +7,6 @@
   "barcode_chevron_up.svg",
   "barcode_copy.svg",
   "barcode_scan_box_border_mask.svg",
-  "barcode_toggle_off.svg",
-  "barcode_toggle_on.svg",
   "barcode_url.svg",
   "camera_button_fps_30_checked.svg",
   "camera_button_fps_30_unchecked.svg",
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
index 27da7306..bb7b4ec 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
@@ -235,7 +235,7 @@
               params.constraints, params.captureResolution,
               assertExists(this.handler));
         },
-        isSupported: async () => state.get(state.State.SHOW_SCAN_MODE),
+        isSupported: async () => true,
         isSupportPTZ: checkSupportPTZForPhotoMode,
         prepareDevice: async (constraints, resolution) => prepareDeviceForPhoto(
             constraints, resolution, CaptureIntent.STILL_CAPTURE),
diff --git a/ash/webui/camera_app_ui/resources/js/i18n_string.ts b/ash/webui/camera_app_ui/resources/js/i18n_string.ts
index b82f9e1..78957480 100644
--- a/ash/webui/camera_app_ui/resources/js/i18n_string.ts
+++ b/ash/webui/camera_app_ui/resources/js/i18n_string.ts
@@ -39,6 +39,8 @@
   DOCUMENT_MODE_DIALOG_GOT_IT = 'document_mode_dialog_got_it',
   DOCUMENT_MODE_DIALOG_MSG = 'document_mode_dialog_msg',
   DOCUMENT_MODE_DIALOG_INTRO_TITLE = 'document_mode_dialog_intro_title',
+  DOWNLOADING_DOCUMENT_SCANNING_FEATURE =
+      'downloading_document_scanning_feature',
   ERROR_MSG_CAMERA_PAUSED = 'error_msg_camera_paused',
   ERROR_MSG_EMPTY_RECORDING = 'error_msg_empty_recording',
   ERROR_MSG_FILE_SYSTEM_FAILED = 'error_msg_file_system_failed',
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
index b97be4a8..24cacdd 100644
--- a/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
+++ b/ash/webui/camera_app_ui/resources/js/mojo/chrome_helper.ts
@@ -18,6 +18,7 @@
   CameraIntentAction,
   CameraUsageOwnershipMonitorCallbackRouter,
   DocumentOutputFormat,
+  DocumentScannerReadyState,
   ExternalScreenMonitorCallbackRouter,
   FileMonitorResult,
   Rotation,
@@ -299,11 +300,23 @@
   }
 
   /**
-   * Returns true if the document mode is supported on the device.
+   * Gets the ready state of the document scanner.
    */
-  async isDocumentModeSupported(): Promise<boolean> {
-    const {isSupported} = await this.remote.isDocumentModeSupported();
-    return isSupported;
+  async getDocumentScannerReadyState():
+      Promise<{supported: boolean, ready: boolean}> {
+    const {readyState} = await this.remote.getDocumentScannerReadyState();
+    return {
+      supported: readyState !== DocumentScannerReadyState.NOT_SUPPORTED,
+      ready: readyState === DocumentScannerReadyState.SUPPORTED_AND_READY,
+    };
+  }
+
+  /**
+   * Waits until the document mode is ready. Returns false if it fails to load.
+   */
+  async waitUntilDocumentModeReady(): Promise<boolean> {
+    const {isLoaded} = await this.remote.registerDocumentScannerReadyCallback();
+    return isLoaded;
   }
 
   /**
diff --git a/ash/webui/camera_app_ui/resources/js/mojo/type.ts b/ash/webui/camera_app_ui/resources/js/mojo/type.ts
index 001de96b..d820002 100644
--- a/ash/webui/camera_app_ui/resources/js/mojo/type.ts
+++ b/ash/webui/camera_app_ui/resources/js/mojo/type.ts
@@ -13,6 +13,7 @@
   CameraAppHelperRemote,
   CameraUsageOwnershipMonitorCallbackRouter,
   DocumentOutputFormat,
+  DocumentScannerReadyState,
   ExternalScreenMonitorCallbackRouter,
   FileMonitorResult,
   ScreenState,
diff --git a/ash/webui/camera_app_ui/resources/js/state.ts b/ash/webui/camera_app_ui/resources/js/state.ts
index b822675..be32a07 100644
--- a/ash/webui/camera_app_ui/resources/js/state.ts
+++ b/ash/webui/camera_app_ui/resources/js/state.ts
@@ -49,7 +49,6 @@
   RECORDING_UI_PAUSED = 'recording-ui-paused',
   SHOULD_HANDLE_INTENT_RESULT = 'should-handle-intent-result',
   SHOW_GIF_RECORDING_OPTION = 'show-gif-recording-option',
-  SHOW_SCAN_MODE = 'show-scan-mode',
   SHUTTER_PROGRESSING = 'shutter-progressing',
   SNAPSHOTTING = 'snapshotting',
   STREAMING = 'streaming',
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.ts b/ash/webui/camera_app_ui/resources/js/views/camera.ts
index 44832c7..5c091e1 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera.ts
@@ -276,7 +276,7 @@
         () => this.cameraManager.reconfigure());
 
     this.initVideoEncoderOptions();
-    await this.initScanMode();
+    this.initScanMode();
   }
 
   /**
@@ -321,13 +321,26 @@
   }
 
   private async initScanMode() {
-    const isPlatformSupport =
-        await ChromeHelper.getInstance().isDocumentModeSupported();
-    state.set(state.State.SHOW_SCAN_MODE, isPlatformSupport);
-    if (!isPlatformSupport) {
+    const {supported, ready} =
+        await ChromeHelper.getInstance().getDocumentScannerReadyState();
+    if (!supported) {
       return;
     }
 
+    const scanModeBtn = dom.get('input[data-mode="scan"]', HTMLInputElement);
+    const scanModeItem =
+        assertInstanceof(scanModeBtn.parentElement, HTMLDivElement);
+    if (!ready) {
+      // TODO(b/226262670): Implement the UI rather than using a regular toast.
+      toast.show(I18nString.DOWNLOADING_DOCUMENT_SCANNING_FEATURE);
+      const isLoaded = await this.scanOptions.waitUntilDocumentModeReady();
+      if (!isLoaded) {
+        return;
+      }
+    } else {
+      this.scanOptions.onDocumentModeReady();
+    }
+
     // Check show toast.
     if (!state.get(state.State.IS_NEW_FEATURE_TOAST_SHOWN) &&
         !localStorage.getBool(LocalStorageKey.DOC_MODE_TOAST_SHOWN)) {
@@ -335,9 +348,6 @@
       localStorage.set(LocalStorageKey.DOC_MODE_TOAST_SHOWN, true);
       // aria-owns don't work on HTMLInputElement, show toast on parent div
       // instead.
-      const scanModeBtn = dom.get('input[data-mode="scan"]', HTMLInputElement);
-      const scanModeItem =
-          assertInstanceof(scanModeBtn.parentElement, HTMLDivElement);
       newFeatureToast.show(scanModeItem);
       scanModeBtn.addEventListener('click', () => {
         newFeatureToast.hide();
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
index 918ab1f..c47438d 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/scan_options.ts
@@ -8,6 +8,7 @@
 import * as dom from '../../dom.js';
 import {sendBarcodeEnabledEvent} from '../../metrics.js';
 import {BarcodeScanner} from '../../models/barcode.js';
+import {ChromeHelper} from '../../mojo/chrome_helper.js';
 import * as state from '../../state.js';
 import {Mode, PreviewVideo} from '../../type.js';
 import {assertEnumVariant} from '../../util.js';
@@ -33,20 +34,12 @@
   return dom.get(`input[data-scantype=${type}]`, HTMLInputElement);
 }
 
-const DEFAULT_SCAN_TYPE = ScanType.DOCUMENT;
-
 type ScanOptionsChangeListener = () => void;
 
 /**
  * Controller for the scan options of Camera view.
  */
 export class ScanOptions implements CameraUI {
-  /**
-   * Togglable barcode option in photo mode.
-   */
-  private readonly photoBarcodeOption =
-      dom.get('#toggle-barcode', HTMLInputElement);
-
   private readonly scanOptions =
       [...dom.getAll('#scan-modes-group [data-scantype]', HTMLInputElement)];
 
@@ -70,18 +63,16 @@
     this.documentCornerOverlay = new DocumentCornerOverlay(
         (p) => this.cameraManager.setPointOfInterest(p));
 
-    for (const option of [this.photoBarcodeOption, ...this.scanOptions]) {
+    // By default, the checked scan type is barcode unless the document mode is
+    // ready.
+    dom.get('#scan-barcode', HTMLInputElement).checked = true;
+
+    for (const option of this.scanOptions) {
       option.addEventListener('click', (evt) => {
         if (state.get(state.State.CAMERA_CONFIGURING)) {
           evt.preventDefault();
         }
       });
-    }
-    this.photoBarcodeOption.addEventListener('change', () => {
-      this.updateOption(
-          this.photoBarcodeOption.checked ? ScanType.BARCODE : null);
-    });
-    for (const option of this.scanOptions) {
       option.addEventListener('change', () => {
         if (option.checked) {
           this.updateOption(this.getToggledScanOption());
@@ -90,6 +81,24 @@
     }
   }
 
+  async waitUntilDocumentModeReady(): Promise<boolean> {
+    const documentModeBtn = dom.get('#scan-document-option', HTMLDivElement);
+    documentModeBtn.hidden = true;
+    const isLoaded =
+        await ChromeHelper.getInstance().waitUntilDocumentModeReady();
+    if (isLoaded) {
+      this.onDocumentModeReady();
+      documentModeBtn.hidden = false;
+    }
+    return isLoaded;
+  }
+
+  onDocumentModeReady(): void {
+    if (!state.get(Mode.SCAN)) {
+      dom.get('#scan-document', HTMLInputElement).checked = true;
+    }
+  }
+
   /**
    * Add listener for scan options change.
    */
@@ -115,7 +124,7 @@
     });
     const {deviceId} = video.getVideoSettings();
     this.documentCornerOverlay.attach(deviceId);
-    const scanType = state.get(Mode.SCAN) ? this.getToggledScanOption() : null;
+    const scanType = this.getToggledScanOption();
     (async () => {
       await video.onExpired;
       this.detachPreview();
@@ -129,8 +138,8 @@
    */
   private getToggledScanOption(): ScanType {
     const checkedEl = this.scanOptions.find(({checked}) => checked);
-    return checkedEl === undefined ? DEFAULT_SCAN_TYPE :
-                                     getScanTypeFromElement(checkedEl);
+    assert(checkedEl !== undefined);
+    return getScanTypeFromElement(checkedEl);
   }
 
   isDocumentModeEnabled(): boolean {
@@ -141,15 +150,14 @@
    * @param scanType Scan type to be enabled, null for no type is
    *     enabled.
    */
-  private async updateOption(scanType: ScanType|null) {
+  private async updateOption(scanType: ScanType) {
     if (!this.previewAvailable()) {
       return;
     }
     assert(this.barcodeScanner !== null);
 
-    this.updateOptionsUI(scanType);
-    const mode = state.get(state.State.SHOW_SCAN_MODE) ? Mode.SCAN : Mode.PHOTO;
-    if (state.get(mode) && scanType === ScanType.BARCODE) {
+    getElementFromScanType(scanType).checked = true;
+    if (state.get(Mode.SCAN) && scanType === ScanType.BARCODE) {
       sendBarcodeEnabledEvent();
       this.barcodeScanner.start();
       state.set(state.State.ENABLE_SCAN_BARCODE, true);
@@ -175,15 +183,6 @@
     state.set(state.State.ENABLE_SCAN_BARCODE, false);
   }
 
-  private updateOptionsUI(scanType: ScanType|null) {
-    if (state.get(Mode.SCAN)) {
-      assert(scanType !== null);
-      getElementFromScanType(scanType).checked = true;
-    } else if (state.get(Mode.PHOTO)) {
-      this.photoBarcodeOption.checked = scanType === ScanType.BARCODE;
-    }
-  }
-
   /**
    * Stops all scanner and detach from current preview.
    */
diff --git a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
index f570ddb..cb4bccf84 100644
--- a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
+++ b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
@@ -695,6 +695,9 @@
       <message desc="Label for spoken feedback to indicate that the mute button is now on." name="IDS_ARIA_MUTE_ON">
         Toggle microphone mute. Mute is on
       </message>
+      <message desc="Toast message notifying user about the document scanner feature is currently downloading" name="IDS_DOWNLOADING_DOCUMENT_SCANNING_FEATURE">
+        Downloading document scanning feature
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/ash/webui/camera_app_ui/resources/strings/camera_strings_grd/IDS_DOWNLOADING_DOCUMENT_SCANNING_FEATURE.png.sha1 b/ash/webui/camera_app_ui/resources/strings/camera_strings_grd/IDS_DOWNLOADING_DOCUMENT_SCANNING_FEATURE.png.sha1
new file mode 100644
index 0000000..e5d73d1
--- /dev/null
+++ b/ash/webui/camera_app_ui/resources/strings/camera_strings_grd/IDS_DOWNLOADING_DOCUMENT_SCANNING_FEATURE.png.sha1
@@ -0,0 +1 @@
+fe44e7853755d47f1aa443edba1dfd719c8295cc
\ No newline at end of file
diff --git a/ash/webui/camera_app_ui/resources/views/main.html b/ash/webui/camera_app_ui/resources/views/main.html
index f1816e2b..9e3853a 100644
--- a/ash/webui/camera_app_ui/resources/views/main.html
+++ b/ash/webui/camera_app_ui/resources/views/main.html
@@ -128,10 +128,6 @@
                i18n-label="toggle_mic_button" data-state="mic"
                i18n-aria="aria_mute_off" data-key="toggleMic" checked>
       </div>
-      <div class="top-stripe right-stripe circle buttons">
-        <input id="toggle-barcode" type="checkbox" tabindex="0"
-               i18n-label="toggle_barcode_button">
-      </div>
       <div id="options-group" class="left-stripe buttons circle">
         <button id="open-mirror-panel" tabindex="0" aria-haspopup="true">
         </button>
diff --git a/ash/wm/base_state.cc b/ash/wm/base_state.cc
index d5e0465..d0766a4 100644
--- a/ash/wm/base_state.cc
+++ b/ash/wm/base_state.cc
@@ -14,7 +14,6 @@
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_positioning_utils.h"
-#include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "ui/aura/client/aura_constants.h"
@@ -210,14 +209,6 @@
 gfx::Rect BaseState::GetSnappedWindowBoundsInParent(
     aura::Window* window,
     const WindowStateType state_type) {
-  return BaseState::GetSnappedWindowBoundsInParent(window, state_type,
-                                                   kDefaultSnapRatio);
-}
-
-gfx::Rect BaseState::GetSnappedWindowBoundsInParent(
-    aura::Window* window,
-    const WindowStateType state_type,
-    float snap_ratio) {
   gfx::Rect bounds_in_parent;
   if (ShouldAllowSplitView()) {
     bounds_in_parent =
@@ -225,15 +216,13 @@
             (state_type == WindowStateType::kPrimarySnapped)
                 ? SplitViewController::LEFT
                 : SplitViewController::RIGHT,
-            window, snap_ratio);
+            window);
   } else {
-    // Use `window_positioning_utils` to calculate the snapped window bounds.
-    bounds_in_parent = ash::GetSnappedWindowBoundsInParent(
-        window,
-        state_type == WindowStateType::kPrimarySnapped
-            ? SnapViewType::kPrimary
-            : SnapViewType::kSecondary,
-        snap_ratio);
+    bounds_in_parent = (state_type == WindowStateType::kPrimarySnapped)
+                           ? GetDefaultSnappedWindowBoundsInParent(
+                                 window, SnapViewType::kPrimary)
+                           : GetDefaultSnappedWindowBoundsInParent(
+                                 window, SnapViewType::kSecondary);
   }
   return bounds_in_parent;
 }
diff --git a/ash/wm/base_state.h b/ash/wm/base_state.h
index 79eb819..5cbfab2 100644
--- a/ash/wm/base_state.h
+++ b/ash/wm/base_state.h
@@ -63,14 +63,6 @@
       aura::Window* window,
       const chromeos::WindowStateType state_type);
 
-  // Returns the window bounds for snapped window state for given `snap_ratio`.
-  // Note that even when `snap_ratio` is provided, it might get ignored to meet
-  // the window's minimum size requirement.
-  gfx::Rect GetSnappedWindowBoundsInParent(
-      aura::Window* window,
-      const chromeos::WindowStateType state_type,
-      float snap_ratio);
-
   // Prepares for the window snap event. Check if the window can be snapped in
   // split screen and if so, SplitViewController will start observe this window.
   // This needs to be done before the window's state and bounds change to its
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc
index a774e38..81cfe85 100644
--- a/ash/wm/default_state.cc
+++ b/ash/wm/default_state.cc
@@ -40,19 +40,6 @@
 namespace ash {
 namespace {
 
-float GetFloatValueForSnapRatio(WindowSnapWMEvent::SnapRatio snap_ratio) {
-  switch (snap_ratio) {
-    case WindowSnapWMEvent::SnapRatio::kOneThirdSnapRatio:
-      return kOneThirdPositionRatio;
-    case WindowSnapWMEvent::SnapRatio::kDefaultSnapRatio:
-      return kDefaultPositionRatio;
-    case WindowSnapWMEvent::SnapRatio::kTwoThirdSnapRatio:
-      return kTwoThirdPositionRatio;
-    default:
-      return kDefaultPositionRatio;
-  }
-}
-
 using ::chromeos::WindowStateType;
 
 // This specifies how much percent (30%) of a window rect
@@ -372,14 +359,10 @@
     HandleWindowSnapping(window_state, type);
 
   if (next_state_type == current_state_type && window_state->IsSnapped()) {
-    float snap_ratio = GetFloatValueForSnapRatio(
-        static_cast<const WindowSnapWMEvent*>(event)->snap_ratio());
-    gfx::Rect snapped_bounds =
-        GetSnappedWindowBoundsInParent(window_state->window(),
-                                       event->type() == WM_EVENT_SNAP_PRIMARY
-                                           ? WindowStateType::kPrimarySnapped
-                                           : WindowStateType::kSecondarySnapped,
-                                       snap_ratio);
+    gfx::Rect snapped_bounds = GetSnappedWindowBoundsInParent(
+        window_state->window(), event->type() == WM_EVENT_SNAP_PRIMARY
+                                    ? WindowStateType::kPrimarySnapped
+                                    : WindowStateType::kSecondarySnapped);
     window_state->SetBoundsDirectAnimated(snapped_bounds);
     return;
   }
@@ -392,15 +375,9 @@
     }
     window_state->RecordAndResetWindowSnapActionSource(current_state_type,
                                                        next_state_type);
-
-    EnterToNextState(
-        window_state, next_state_type,
-        absl::make_optional(
-            static_cast<const WindowSnapWMEvent*>(event)->snap_ratio()));
-    return;
   }
 
-  EnterToNextState(window_state, next_state_type, absl::nullopt);
+  EnterToNextState(window_state, next_state_type);
 }
 
 // static
@@ -448,10 +425,8 @@
   }
 }
 
-void DefaultState::EnterToNextState(
-    WindowState* window_state,
-    WindowStateType next_state_type,
-    absl::optional<WindowSnapWMEvent::SnapRatio> snap_ratio) {
+void DefaultState::EnterToNextState(WindowState* window_state,
+                                    WindowStateType next_state_type) {
   // Do nothing if  we're already in the same state.
   if (state_type_ == next_state_type)
     return;
@@ -502,11 +477,7 @@
     if (window_state->IsMaximizedOrFullscreenOrPinned())
       MoveToDisplayForRestore(window_state);
 
-    UpdateBoundsFromState(
-        window_state, previous_state_type,
-        snap_ratio.has_value()
-            ? absl::make_optional(GetFloatValueForSnapRatio(snap_ratio.value()))
-            : absl::nullopt);
+    UpdateBoundsFromState(window_state, previous_state_type);
     UpdateMinimizedState(window_state, previous_state_type);
 
     // Normal state should have no restore bounds unless it's
@@ -549,9 +520,7 @@
     window_state->SetRestoreBoundsInParent(stored_bounds_);
   }
 
-  // When reentering a state, use the saved `snap_ratio_`.
-  UpdateBoundsFromState(window_state, state_in_previous_mode->GetType(),
-                        window_state->snap_ratio());
+  UpdateBoundsFromState(window_state, state_in_previous_mode->GetType());
   UpdateMinimizedState(window_state, state_in_previous_mode->GetType());
 
   // Then restore the restore bounds to their previous value.
@@ -564,20 +533,14 @@
 }
 
 void DefaultState::UpdateBoundsFromState(WindowState* window_state,
-                                         WindowStateType previous_state_type,
-                                         absl::optional<float> snap_ratio) {
+                                         WindowStateType previous_state_type) {
   aura::Window* window = window_state->window();
   gfx::Rect bounds_in_parent;
-
   switch (state_type_) {
-    // TODO(crbug.com/1335500): Refactor snap state type handling. Since only
-    // snap state types define `snap_ratio`, it makes sense to handle them
-    // separately.
     case WindowStateType::kPrimarySnapped:
     case WindowStateType::kSecondarySnapped:
-      DCHECK(snap_ratio.has_value());
-      bounds_in_parent = GetSnappedWindowBoundsInParent(
-          window_state->window(), state_type_, snap_ratio.value());
+      bounds_in_parent =
+          GetSnappedWindowBoundsInParent(window_state->window(), state_type_);
       base::UmaHistogramEnumeration(
           kSnapWindowDeviceOrientationHistogramName,
           chromeos::IsDisplayLayoutHorizontal(
diff --git a/ash/wm/default_state.h b/ash/wm/default_state.h
index 9e89f28..03dbc4e 100644
--- a/ash/wm/default_state.h
+++ b/ash/wm/default_state.h
@@ -50,12 +50,9 @@
                         const SetBoundsWMEvent* bounds_event);
 
   // Enters next state. This is used when the state moves from one to another
-  // within the same desktop mode. Uses `snap_ratio` for the next state type if
-  // provided.
-  void EnterToNextState(
-      WindowState* window_state,
-      chromeos::WindowStateType next_state_type,
-      absl::optional<WindowSnapWMEvent::SnapRatio> snap_ratio);
+  // within the same desktop mode.
+  void EnterToNextState(WindowState* window_state,
+                        chromeos::WindowStateType next_state_type);
 
   // Reenters the current state. This is called when migrating from
   // previous desktop mode, and the window's state needs to re-construct the
@@ -63,11 +60,9 @@
   void ReenterToCurrentState(WindowState* window_state,
                              WindowState::State* state_in_previous_mode);
 
-  // Animates to new window bounds, using `snap_ratio` if provided, based on the
-  // current and previous state type.
+  // Animates to new window bounds based on the current and previous state type.
   void UpdateBoundsFromState(WindowState* window_state,
-                             chromeos::WindowStateType old_state_type,
-                             absl::optional<float> snap_ratio);
+                             chromeos::WindowStateType old_state_type);
 
   // Updates the window bounds for display bounds, or display work area bounds
   // changes.
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 3036d80e..848bddf 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -4271,7 +4271,8 @@
   // given |window| is equal to the given |expected_visibility|.
   void VerifyViewVisibility(aura::Window* window,
                             bool expected_visibility) const {
-    const size_t index = GetShelfItemIndexForWindow(window);
+    const int index = GetShelfItemIndexForWindow(window);
+    EXPECT_GE(index, 0);
     auto* shelf_view = GetShelfView();
     auto* view_model = shelf_view->view_model();
 
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index c1f18c4..53afaa0 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -88,6 +88,11 @@
 // always be moved to these three positions.
 constexpr float kFixedPositionRatios[] = {0.f, 0.5f, 1.0f};
 
+// Two optional position ratios of the divider. Whether the divider can be moved
+// to these two positions depends on the minimum size of the snapped windows.
+constexpr float kOneThirdPositionRatio = 0.33f;
+constexpr float kTwoThirdPositionRatio = 0.67f;
+
 // The black scrim starts to fade in when the divider is moved past the two
 // optional positions (kOneThirdPositionRatio, kTwoThirdPositionRatio) and
 // reaches to its maximum opacity (kBlackScrimOpacity) after moving
@@ -1033,25 +1038,16 @@
 
 gfx::Rect SplitViewController::GetSnappedWindowBoundsInParent(
     SnapPosition snap_position,
-    aura::Window* window_for_minimum_size,
-    float snap_ratio) {
-  gfx::Rect bounds = GetSnappedWindowBoundsInScreen(
-      snap_position, window_for_minimum_size, snap_ratio);
+    aura::Window* window_for_minimum_size) {
+  gfx::Rect bounds =
+      GetSnappedWindowBoundsInScreen(snap_position, window_for_minimum_size);
   wm::ConvertRectFromScreen(root_window_, &bounds);
   return bounds;
 }
 
-gfx::Rect SplitViewController::GetSnappedWindowBoundsInParent(
-    SnapPosition snap_position,
-    aura::Window* window_for_minimum_size) {
-  return GetSnappedWindowBoundsInParent(snap_position, window_for_minimum_size,
-                                        kDefaultSnapRatio);
-}
-
 gfx::Rect SplitViewController::GetSnappedWindowBoundsInScreen(
     SnapPosition snap_position,
-    aura::Window* window_for_minimum_size,
-    float snap_ratio) {
+    aura::Window* window_for_minimum_size) {
   const gfx::Rect work_area_bounds_in_screen =
       screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
           root_window_);
@@ -1073,8 +1069,8 @@
   // mode to `GetSnappedWindowBounds()` in window_positioning_utils.cc.
   const bool in_tablet = Shell::Get()->tablet_mode_controller()->InTabletMode();
   const int work_area_size = GetDividerEndPosition();
-  int divider_position = divider_position_ < 0 ? GetDividerPosition(snap_ratio)
-                                               : divider_position_;
+  int divider_position =
+      divider_position_ < 0 ? GetDefaultDividerPosition() : divider_position_;
 
   // Edit `divider_position` if window restore is currently restoring a snapped
   // window; take into account the snap percentage saved by the window. Only do
@@ -1157,26 +1153,15 @@
   return snapped_window_bounds_in_screen;
 }
 
-gfx::Rect SplitViewController::GetSnappedWindowBoundsInScreen(
-    SnapPosition snap_position,
-    aura::Window* window_for_minimum_size) {
-  return GetSnappedWindowBoundsInScreen(snap_position, window_for_minimum_size,
-                                        kDefaultSnapRatio);
-}
-
 bool SplitViewController::ShouldUseWindowBoundsDuringFastResize() {
   return is_resizing_ && tablet_resize_mode_ == TabletResizeMode::kFast;
 }
 
 int SplitViewController::GetDefaultDividerPosition() const {
-  return GetDividerPosition(kDefaultPositionRatio);
-}
-
-int SplitViewController::GetDividerPosition(float snap_ratio) const {
-  int next_divider_position = GetDividerEndPosition() * snap_ratio;
+  int default_divider_position = GetDividerEndPosition() / 2;
   if (split_view_type_ == SplitViewType::kTabletType)
-    next_divider_position -= kSplitviewDividerShortSideLength / 2;
-  return next_divider_position;
+    default_divider_position -= kSplitviewDividerShortSideLength / 2;
+  return default_divider_position;
 }
 
 bool SplitViewController::IsDividerAnimating() const {
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 7d144b2..b449ba0c 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -213,30 +213,12 @@
   // |left_window_|. All the other window will open on the right side.
   aura::Window* GetDefaultSnappedWindow();
 
-  // Gets snapped bounds based on |snap_position|, the side of the screen to
-  // snap to, and |snap_ratio|, the ratio of the screen that the snapped window
-  // will occupy, adjusted to accommodate the minimum size of
-  // |window_for_minimum_size| if |window_for_minimum_size| is not null.
-  gfx::Rect GetSnappedWindowBoundsInParent(
-      SnapPosition snap_position,
-      aura::Window* window_for_minimum_size,
-      float snap_ratio);
-
-  // Gets snapped bounds based on |snap_position|, |divider_position_|, and
-  // |kDefaultSnapRatio|, adjusted to accommodate the minimum size of
-  // |window_for_minimum_size| if |window_for_minimum_size| is not null.
+  // Gets snapped bounds based on |snap_position| and |divider_position_|,
+  // adjusted to accommodate the minimum size of |window_for_minimum_size| if
+  // |window_for_minimum_size| is not null.
   gfx::Rect GetSnappedWindowBoundsInParent(
       SnapPosition snap_position,
       aura::Window* window_for_minimum_size);
-
-  // Gets snapped bounds in screen coordinates based on |snap_position| and
-  // |snap_ratio|.
-  gfx::Rect GetSnappedWindowBoundsInScreen(
-      SnapPosition snap_position,
-      aura::Window* window_for_minimum_size,
-      float snap_ratio);
-
-  // Gets snapped bounds in screen coordinates for |kDefaultSnapRatio|.
   gfx::Rect GetSnappedWindowBoundsInScreen(
       SnapPosition snap_position,
       aura::Window* window_for_minimum_size);
@@ -249,11 +231,6 @@
   // Gets the default value of |divider_position_|.
   int GetDefaultDividerPosition() const;
 
-  // Calculates the new divider position to move |divider_position_| to, such
-  // that the primary window will occupy |snap_ratio| of the screen, and the
-  // secondary window will occupy the rest.
-  int GetDividerPosition(float snap_ratio) const;
-
   // Returns true during the divider snap animation.
   bool IsDividerAnimating() const;
 
diff --git a/ash/wm/window_positioning_utils.cc b/ash/wm/window_positioning_utils.cc
index ccb8b38..481d3c9 100644
--- a/ash/wm/window_positioning_utils.cc
+++ b/ash/wm/window_positioning_utils.cc
@@ -108,18 +108,12 @@
                                        kMinimumOnScreenArea, bounds);
 }
 
-gfx::Rect GetSnappedWindowBoundsInParent(aura::Window* window,
-                                         SnapViewType type,
-                                         float snap_ratio) {
+gfx::Rect GetDefaultSnappedWindowBoundsInParent(aura::Window* window,
+                                                SnapViewType type) {
   return GetSnappedWindowBounds(
       screen_util::GetDisplayWorkAreaBoundsInParent(window),
       display::Screen::GetScreen()->GetDisplayNearestWindow(window), window,
-      type, snap_ratio);
-}
-
-gfx::Rect GetDefaultSnappedWindowBoundsInParent(aura::Window* window,
-                                                SnapViewType type) {
-  return GetSnappedWindowBoundsInParent(window, type, kDefaultPositionRatio);
+      type, kDefaultSnapRatio);
 }
 
 gfx::Rect GetSnappedWindowBounds(const gfx::Rect& work_area,
diff --git a/ash/wm/window_positioning_utils.h b/ash/wm/window_positioning_utils.h
index 5ddb491..171127b 100644
--- a/ash/wm/window_positioning_utils.h
+++ b/ash/wm/window_positioning_utils.h
@@ -55,14 +55,8 @@
     const gfx::Rect& visible_area,
     gfx::Rect* bounds);
 
-// Returns the bounds of a snapped window for a given snap |type| and
-// |snap_ratio| in clamshell mode.
-ASH_EXPORT gfx::Rect GetSnappedWindowBoundsInParent(aura::Window* window,
-                                                    SnapViewType type,
-                                                    float snap_ratio);
-
-// Returns the bounds of a snapped window with default snapped ratio
-// |kDefaultSnapRatio|, in parent coordinates.
+// Returns the bounds of a snapped window for a given snap |type| in clamshell
+// mode, with default snapped ratio |kDefaultSnapRatio|, in parent coordinates.
 ASH_EXPORT gfx::Rect GetDefaultSnappedWindowBoundsInParent(aura::Window* window,
                                                            SnapViewType type);
 
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index e4b4b1a2..11ff045 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -43,11 +43,6 @@
 class WindowStateObserver;
 class WMEvent;
 
-// TODO(crbug.com/1323394): Consider moving to a WindowState constants file.
-constexpr float kOneThirdPositionRatio = 0.33f;
-constexpr float kDefaultPositionRatio = 0.5f;
-constexpr float kTwoThirdPositionRatio = 0.67f;
-
 // WindowState manages and defines ash specific window state and
 // behavior. Ash specific per-window state (such as ones that controls
 // window manager behavior) and ash specific window behavior (such as
diff --git a/ash/wm/wm_event.cc b/ash/wm/wm_event.cc
index c86a02e..1fb408f7 100644
--- a/ash/wm/wm_event.cc
+++ b/ash/wm/wm_event.cc
@@ -85,19 +85,6 @@
   return false;
 }
 
-bool WMEvent::IsSnapEvent() const {
-  switch (type_) {
-    case WM_EVENT_SNAP_PRIMARY:
-    case WM_EVENT_SNAP_SECONDARY:
-    case WM_EVENT_CYCLE_SNAP_PRIMARY:
-    case WM_EVENT_CYCLE_SNAP_SECONDARY:
-      return true;
-    default:
-      break;
-  }
-  return false;
-}
-
 const DisplayMetricsChangedWMEvent* WMEvent::AsDisplayMetricsChangedWMEvent()
     const {
   DCHECK_EQ(type(), WM_EVENT_DISPLAY_BOUNDS_CHANGED);
@@ -121,18 +108,6 @@
 
 SetBoundsWMEvent::~SetBoundsWMEvent() = default;
 
-WindowSnapWMEvent::WindowSnapWMEvent(WMEventType type) : WMEvent(type) {
-  DCHECK(IsSnapEvent());
-}
-
-WindowSnapWMEvent::WindowSnapWMEvent(WMEventType type,
-                                     WindowSnapWMEvent::SnapRatio snap_ratio)
-    : WMEvent(type), snap_ratio_(snap_ratio) {
-  DCHECK(IsSnapEvent());
-}
-
-WindowSnapWMEvent::~WindowSnapWMEvent() = default;
-
 DisplayMetricsChangedWMEvent::DisplayMetricsChangedWMEvent(int changed_metrics)
     : WMEvent(WM_EVENT_DISPLAY_BOUNDS_CHANGED),
       changed_metrics_(changed_metrics) {}
diff --git a/ash/wm/wm_event.h b/ash/wm/wm_event.h
index 335bc87..7f77c4f5 100644
--- a/ash/wm/wm_event.h
+++ b/ash/wm/wm_event.h
@@ -155,9 +155,6 @@
   // e.g. WM_EVENT_MAXIMIZED.
   bool IsTransitionEvent() const;
 
-  // True if the event is a window snap event.
-  bool IsSnapEvent() const;
-
   // Utility methods to downcast to specific WMEvent types.
   const DisplayMetricsChangedWMEvent* AsDisplayMetricsChangedWMEvent() const;
 
@@ -194,29 +191,6 @@
   const base::TimeDelta duration_;
 };
 
-// An WMEvent to snap a window.
-class WindowSnapWMEvent : public WMEvent {
- public:
-  enum class SnapRatio {
-    kOneThirdSnapRatio,
-    kDefaultSnapRatio,
-    kTwoThirdSnapRatio
-  };
-
-  explicit WindowSnapWMEvent(WMEventType type);
-  WindowSnapWMEvent(WMEventType type, SnapRatio snap_ratio);
-
-  WindowSnapWMEvent(const WindowSnapWMEvent&) = delete;
-  WindowSnapWMEvent& operator=(const WindowSnapWMEvent&) = delete;
-
-  ~WindowSnapWMEvent() override;
-
-  SnapRatio snap_ratio() const { return snap_ratio_; }
-
- private:
-  const SnapRatio snap_ratio_ = SnapRatio::kDefaultSnapRatio;
-};
-
 // A WMEvent sent when display metrics have changed.
 // TODO(oshima): Consolidate with WM_EVENT_WORKAREA_BOUNDS_CHANGED.
 class ASH_EXPORT DisplayMetricsChangedWMEvent : public WMEvent {
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 9900cb4..9ee16f9 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220623.2.1
+8.20220624.1.1
diff --git a/build/fuchsia/test/common.py b/build/fuchsia/test/common.py
index abccff6..229673f9 100644
--- a/build/fuchsia/test/common.py
+++ b/build/fuchsia/test/common.py
@@ -143,6 +143,15 @@
                                    'the default target in ffx will be used.'))
 
 
+def register_log_args(parser: ArgumentParser) -> None:
+    """Register commonly used arguments."""
+
+    log_args = parser.add_argument_group('logging', 'logging arguments')
+    log_args.add_argument('--logs-dir',
+                          type=os.path.realpath,
+                          help=('Directory to write logs to.'))
+
+
 def get_component_uri(package: str) -> str:
     """Retrieve the uri for a package."""
     return f'fuchsia-pkg://{REPO_ALIAS}/{package}#meta/{package}.cm'
diff --git a/build/fuchsia/test/ffx_integration.py b/build/fuchsia/test/ffx_integration.py
index 77072c4..d838f48 100644
--- a/build/fuchsia/test/ffx_integration.py
+++ b/build/fuchsia/test/ffx_integration.py
@@ -61,8 +61,7 @@
                 # doesn't already restore the old value.
                 if  self._old_value != \
                     run_ffx_command(['config', 'get', self._name],
-                                    capture_output=True,
-                                    encoding='utf-8').stdout.strip():
+                                    capture_output=True).stdout.strip():
                     run_ffx_command(
                         ['config', 'set', self._name, self._old_value])
 
@@ -79,8 +78,12 @@
 class FfxEmulator(AbstractContextManager):
     """A helper for managing emulators."""
 
-    def __init__(self, enable_graphics: bool, hardware_gpu: bool,
-                 product_bundle: Optional[str], with_network: bool) -> None:
+    def __init__(self,
+                 enable_graphics: bool,
+                 hardware_gpu: bool,
+                 product_bundle: Optional[str],
+                 with_network: bool,
+                 logs_dir: Optional[str] = None) -> None:
         if product_bundle:
             self._product_bundle = product_bundle
         else:
@@ -89,10 +92,16 @@
 
         self._enable_graphics = enable_graphics
         self._hardware_gpu = hardware_gpu
+        self._logs_dir = logs_dir
         self._with_network = with_network
         node_name_suffix = random.randint(1, 9999)
         self._node_name = f'fuchsia-emulator-{node_name_suffix}'
 
+        # Always set the download path parallel to Fuchsia SDK directory
+        # so that old product bundles can be properly removed.
+        self._scoped_pb_storage = ScopedFfxConfig(
+            'pbms.storage.path', os.path.join(SDK_ROOT, os.pardir, 'images'))
+
     @staticmethod
     def _check_ssh_config_file() -> None:
         """Checks for ssh keys and generates them if they are missing."""
@@ -110,8 +119,11 @@
         # redownload.
         list_cmd = run_ffx_command(('product-bundle', 'list'),
                                    capture_output=True)
+        sdk_version = run_ffx_command(('sdk', 'version'),
+                                      capture_output=True).stdout.strip()
         for line in list_cmd.stdout.splitlines():
-            if self._product_bundle in line and '*' in line:
+            if (self._product_bundle in line and sdk_version in line
+                    and '*' in line):
                 return
 
         run_ffx_command(('product-bundle', 'get', self._product_bundle))
@@ -123,6 +135,7 @@
             The node name of the emulator.
         """
 
+        self._scoped_pb_storage.__enter__()
         self._check_ssh_config_file()
         self._download_product_bundle_if_necessary()
         emu_command = [
@@ -132,6 +145,9 @@
             emu_command.append('-H')
         if self._hardware_gpu:
             emu_command.append('--gpu')
+        if self._logs_dir:
+            emu_command.extend(
+                ('-l', os.path.join(self._logs_dir, 'emulator_log')))
         if self._with_network:
             emu_command.extend(('--net', 'tap'))
         run_ffx_command(emu_command)
@@ -144,6 +160,8 @@
         # might fail.
         run_ffx_command(('emu', 'stop', self._node_name), check=False)
 
+        self._scoped_pb_storage.__exit__(exc_type, exc_value, traceback)
+
         # Do not suppress exceptions.
         return False
 
diff --git a/build/fuchsia/test/log_manager.py b/build/fuchsia/test/log_manager.py
index 9280532..1458e5f 100755
--- a/build/fuchsia/test/log_manager.py
+++ b/build/fuchsia/test/log_manager.py
@@ -10,7 +10,6 @@
 import sys
 import time
 
-from argparse import ArgumentParser
 from contextlib import AbstractContextManager
 from typing import Iterable, Optional, TextIO
 
@@ -115,15 +114,6 @@
             run_continuous_ffx_command(log_cmd, stdout=system_log))
 
 
-def register_log_args(parser: ArgumentParser) -> None:
-    """Register commonly used arguments."""
-
-    log_args = parser.add_argument_group('logging', 'logging arguments')
-    log_args.add_argument('--logs-dir',
-                          type=os.path.realpath,
-                          help=('Directory to write logs to.'))
-
-
 def main():
     """Stand-alone function for fetching system logs and print to terminal.
     Runs until the process is killed or interrupted (i.e. user presses CTRL-C).
diff --git a/build/fuchsia/test/pylintrc b/build/fuchsia/test/pylintrc
index 697f5324..0ce3e35 100644
--- a/build/fuchsia/test/pylintrc
+++ b/build/fuchsia/test/pylintrc
@@ -11,4 +11,9 @@
 
 [REPORTS]
 
-reports=no
\ No newline at end of file
+reports=no
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=6
diff --git a/build/fuchsia/test/run_executable_test.py b/build/fuchsia/test/run_executable_test.py
index 84cc2f69..8718555 100755
--- a/build/fuchsia/test/run_executable_test.py
+++ b/build/fuchsia/test/run_executable_test.py
@@ -14,7 +14,7 @@
 from typing import List, Optional
 
 from common import get_component_uri, register_common_args, \
-                   register_device_args, resolve_packages, run_ffx_command
+                   register_device_args, register_log_args, run_ffx_command
 from compatible_utils import map_filter_file_to_package_file
 from ffx_integration import FfxTestRunner
 from test_runner import TestRunner
@@ -23,6 +23,7 @@
 def _copy_custom_output_file(test_runner: FfxTestRunner, file: str,
                              dest: str) -> None:
     """Copy custom test output file from the device to the host."""
+
     artifact_dir = test_runner.get_custom_artifact_directory()
     if not artifact_dir:
         logging.error(
@@ -35,12 +36,17 @@
 class ExecutableTestRunner(TestRunner):
     """Test runner for running standalone test executables."""
 
-    def __init__(self, out_dir: str, test_args: List[str], test_name: str,
-                 target_id: Optional[str]) -> None:
+    def __init__(self,
+                 out_dir: str,
+                 test_args: List[str],
+                 test_name: str,
+                 target_id: Optional[str],
+                 logs_dir: Optional[str] = None) -> None:
         super().__init__(out_dir, test_args, [test_name], target_id)
         self._test_name = test_name
         self._custom_artifact_directory = None
         self._isolated_script_test_output = None
+        self._logs_dir = logs_dir
         self._test_launcher_summary_output = None
 
     def _get_args(self) -> List[str]:
@@ -70,7 +76,9 @@
             '--test-launcher-filter-file',
             help='Filter file(s) passed to target test process. Use ";" to '
             'separate multiple filter files.')
-
+        parser.add_argument('test_process_args',
+                            nargs='*',
+                            help='Arguments for the test process.')
         args, child_args = parser.parse_known_args(self._test_args)
         if args.isolated_script_test_output:
             self._isolated_script_test_output = args.isolated_script_test_output
@@ -95,6 +103,7 @@
                 args.test_launcher_filter_file.split(';'))
             child_args.append('--test-launcher-filter-file=' +
                               ';'.join(test_launcher_filter_files))
+        child_args.extend(args.test_process_args)
         return child_args
 
     def _postprocess(self, test_runner: FfxTestRunner) -> None:
@@ -110,9 +119,8 @@
                 self._isolated_script_test_output)
 
     def run_test(self) -> subprocess.Popen:
-        resolve_packages(self.packages, self._target_id)
         test_args = self._get_args()
-        with FfxTestRunner() as test_runner:
+        with FfxTestRunner(self._logs_dir) as test_runner:
             test_proc = test_runner.run_test(
                 get_component_uri(self._test_name), test_args, self._target_id)
 
@@ -137,23 +145,35 @@
         return test_proc
 
 
-def register_gtest_args(parser: argparse.ArgumentParser) -> None:
-    """Register common arguments for GtestRunner."""
+def create_executable_test_runner(runner_args: argparse.Namespace,
+                                  test_args: List[str]):
+    """Helper for creating an ExecutableTestRunner."""
+
+    return ExecutableTestRunner(runner_args.out_dir, test_args,
+                                runner_args.test_type, runner_args.target_id,
+                                runner_args.logs_dir)
+
+
+def register_test_args(parser: argparse.ArgumentParser) -> None:
+    """Register common arguments for ExecutableTestRunner."""
+
     test_args = parser.add_argument_group('test', 'arguments for test running')
     test_args.add_argument('--test-name',
+                           dest='test_type',
                            help='Name of the test package (e.g. unit_tests).')
 
 
 def main():
-    """Stand-alone function for running gtests."""
+    """Stand-alone function for running executable tests."""
+
     parser = argparse.ArgumentParser()
-    register_gtest_args(parser)
     register_common_args(parser)
     register_device_args(parser)
+    register_log_args(parser)
+    register_test_args(parser)
     runner_args, test_args = parser.parse_known_args()
-    runner = ExecutableTestRunner(runner_args.out_dir, runner_args.test_name,
-                                  test_args, runner_args.target_id)
-    return runner.run_test()
+    runner = create_executable_test_runner(runner_args, test_args)
+    return runner.run_test().returncode
 
 
 if __name__ == '__main__':
diff --git a/build/fuchsia/test/run_test.py b/build/fuchsia/test/run_test.py
index 9ad8360..921c5b3 100755
--- a/build/fuchsia/test/run_test.py
+++ b/build/fuchsia/test/run_test.py
@@ -11,12 +11,13 @@
 from contextlib import ExitStack
 from typing import List
 
-from common import register_common_args, register_device_args
+from common import register_common_args, register_device_args, \
+                   register_log_args, resolve_packages
 from ffx_integration import test_connection
-from log_manager import LogManager, register_log_args, start_system_log
+from log_manager import LogManager, start_system_log
 from publish_package import publish_packages, register_package_args
 from run_blink_test import BlinkTestRunner
-from run_executable_test import ExecutableTestRunner
+from run_executable_test import create_executable_test_runner
 from serve_repo import register_serve_args, serve_repository
 from start_emulator import create_emulator_from_args, register_emulator_args
 from test_runner import TestRunner
@@ -28,8 +29,7 @@
     if runner_args.test_type == 'blink':
         return BlinkTestRunner(runner_args.out_dir, test_args,
                                runner_args.target_id)
-    return ExecutableTestRunner(runner_args.out_dir, test_args,
-                                runner_args.test_type, runner_args.target_id)
+    return create_executable_test_runner(runner_args, test_args)
 
 
 def main():
@@ -72,10 +72,10 @@
         test_connection(runner_args.target_id)
 
         test_runner = _get_test_runner(runner_args, test_args)
-        packages = test_runner.get_package_paths()
+        package_paths = test_runner.get_package_paths()
 
         # Start system logging.
-        start_system_log(log_manager, False, packages, ('--since', 'now'),
+        start_system_log(log_manager, False, package_paths, ('--since', 'now'),
                          runner_args.target_id)
 
         if not runner_args.repo:
@@ -83,10 +83,11 @@
             runner_args.repo = stack.enter_context(
                 tempfile.TemporaryDirectory())
 
-        publish_packages(packages, runner_args.repo,
+        publish_packages(package_paths, runner_args.repo,
                          not runner_args.no_repo_init)
 
         with serve_repository(runner_args):
+            resolve_packages(test_runner.packages, runner_args.target_id)
             return test_runner.run_test().returncode
 
 
diff --git a/build/fuchsia/test/start_emulator.py b/build/fuchsia/test/start_emulator.py
index a5704515..10a0aad7 100755
--- a/build/fuchsia/test/start_emulator.py
+++ b/build/fuchsia/test/start_emulator.py
@@ -9,6 +9,7 @@
 import sys
 import time
 
+from common import register_log_args
 from ffx_integration import FfxEmulator
 
 
@@ -40,13 +41,14 @@
     """Helper method for initializing an FfxEmulator class with parsed
     arguments."""
     return FfxEmulator(args.enable_graphics, args.hardware_gpu,
-                       args.product_bundle, args.with_network)
+                       args.product_bundle, args.with_network, args.logs_dir)
 
 
 def main():
     """Stand-alone function for starting an emulator."""
     parser = argparse.ArgumentParser()
     register_emulator_args(parser)
+    register_log_args(parser)
     args = parser.parse_args()
     with create_emulator_from_args(args):
         try:
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index f632c82..63c11d54 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -70,6 +70,10 @@
   "java/src/org/chromium/chrome/browser/app/appmenu/IncognitoMenuItemViewBinder.java",
   "java/src/org/chromium/chrome/browser/app/appmenu/UpdateMenuItemViewBinder.java",
   "java/src/org/chromium/chrome/browser/app/bluetooth/BluetoothNotificationServiceImpl.java",
+  "java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java",
+  "java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java",
+  "java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditActivity.java",
+  "java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java",
   "java/src/org/chromium/chrome/browser/app/download/DownloadMessageUiDelegate.java",
   "java/src/org/chromium/chrome/browser/app/download/home/DownloadActivity.java",
   "java/src/org/chromium/chrome/browser/app/download/home/DownloadManagerCoordinatorFactoryHelper.java",
@@ -169,15 +173,10 @@
   "java/src/org/chromium/chrome/browser/background_task_scheduler/ChromeNativeBackgroundTaskDelegate.java",
   "java/src/org/chromium/chrome/browser/background_task_scheduler/ProxyNativeTask.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java",
-  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkActivity.java",
-  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddActivity.java",
-  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java",
-  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkFeatures.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderRow.java",
-  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderSelectActivity.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java",
@@ -1143,6 +1142,7 @@
   "java/src/org/chromium/chrome/browser/ui/BottomContainer.java",
   "java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java",
   "java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlocker.java",
+  "java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerFactory.java",
   "java/src/org/chromium/chrome/browser/ui/MediaCaptureOverlayController.java",
   "java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java",
   "java/src/org/chromium/chrome/browser/ui/TabObscuringHandler.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index f26c5ac..fd23914 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -52,6 +52,8 @@
   "javatests/src/org/chromium/chrome/browser/app/ContextMenuDragTest.java",
   "javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java",
+  "javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditTest.java",
+  "javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java",
   "javatests/src/org/chromium/chrome/browser/app/metrics/TabbedActivityLaunchCauseMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/app/tab_activity_glue/TabletPhoneLayoutChangeTest.java",
   "javatests/src/org/chromium/chrome/browser/app/tabmodel/ChromeNextTabPolicySupplierTest.java",
@@ -70,13 +72,11 @@
   "javatests/src/org/chromium/chrome/browser/background_sync/BackgroundSyncTest.java",
   "javatests/src/org/chromium/chrome/browser/background_sync/PeriodicBackgroundSyncTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java",
-  "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkEditTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java",
-  "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRowRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipListRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index 0929e60..5c4ed3d 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -187,6 +187,35 @@
         android:taskAffinity=""
         android:theme="@style/Theme.BrowserUI.NoDisplay">
     </activity>  # DIFF-ANCHOR: 76686af9
+    <activity  # DIFF-ANCHOR: dc170161
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
+    </activity>  # DIFF-ANCHOR: dc170161
+    <activity  # DIFF-ANCHOR: d8508de6
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge">
+    </activity>  # DIFF-ANCHOR: d8508de6
+    <activity  # DIFF-ANCHOR: 766e66bd
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/edit_bookmark"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateHidden">
+    </activity>  # DIFF-ANCHOR: 766e66bd
+    <activity  # DIFF-ANCHOR: 8f8d0c81
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/bookmark_choose_folder"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateAlwaysHidden">
+    </activity>  # DIFF-ANCHOR: 8f8d0c81
     <activity  # DIFF-ANCHOR: 8f3f76d9
         android:name="org.chromium.chrome.browser.app.download.home.DownloadActivity"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
@@ -232,47 +261,6 @@
         android:exported="false"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
     </activity>  # DIFF-ANCHOR: 170e9f21
-    <activity  # DIFF-ANCHOR: da2eedc8
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: da2eedc8
-    <activity  # DIFF-ANCHOR: a208e726
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:excludeFromRecents="true"
-        android:exported="true"
-        android:theme="@style/Theme.BrowserUI.NoDisplay"
-        android:windowSoftInputMode="stateHidden">
-      <intent-filter>  # DIFF-ANCHOR: 47a8059b
-        <action android:name="$PACKAGE.ADDBOOKMARK"/>
-        <category android:name="android.intent.category.DEFAULT"/>
-      </intent-filter>  # DIFF-ANCHOR: 47a8059b
-    </activity>  # DIFF-ANCHOR: a208e726
-    <activity  # DIFF-ANCHOR: e0427380
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge">
-    </activity>  # DIFF-ANCHOR: e0427380
-    <activity  # DIFF-ANCHOR: b66ce3f2
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/edit_bookmark"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateHidden">
-    </activity>  # DIFF-ANCHOR: b66ce3f2
-    <activity  # DIFF-ANCHOR: 0a21ad35
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/bookmark_choose_folder"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateAlwaysHidden">
-    </activity>  # DIFF-ANCHOR: 0a21ad35
     <activity  # DIFF-ANCHOR: be2dce69
         android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
         android:excludeFromRecents="true"
diff --git a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
index 6712c46..8aa262a0 100644
--- a/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__chrome.AndroidManifest.expected
@@ -60,6 +60,35 @@
         android:taskAffinity=""
         android:theme="@style/Theme.BrowserUI.NoDisplay">
     </activity>  # DIFF-ANCHOR: 76686af9
+    <activity  # DIFF-ANCHOR: dc170161
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
+    </activity>  # DIFF-ANCHOR: dc170161
+    <activity  # DIFF-ANCHOR: d8508de6
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge">
+    </activity>  # DIFF-ANCHOR: d8508de6
+    <activity  # DIFF-ANCHOR: 766e66bd
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/edit_bookmark"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateHidden">
+    </activity>  # DIFF-ANCHOR: 766e66bd
+    <activity  # DIFF-ANCHOR: 8f8d0c81
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/bookmark_choose_folder"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateAlwaysHidden">
+    </activity>  # DIFF-ANCHOR: 8f8d0c81
     <activity  # DIFF-ANCHOR: 8f3f76d9
         android:name="org.chromium.chrome.browser.app.download.home.DownloadActivity"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
@@ -105,47 +134,6 @@
         android:exported="false"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
     </activity>  # DIFF-ANCHOR: 170e9f21
-    <activity  # DIFF-ANCHOR: da2eedc8
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: da2eedc8
-    <activity  # DIFF-ANCHOR: a208e726
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:excludeFromRecents="true"
-        android:exported="true"
-        android:theme="@style/Theme.BrowserUI.NoDisplay"
-        android:windowSoftInputMode="stateHidden">
-      <intent-filter>  # DIFF-ANCHOR: 47a8059b
-        <action android:name="$PACKAGE.ADDBOOKMARK"/>
-        <category android:name="android.intent.category.DEFAULT"/>
-      </intent-filter>  # DIFF-ANCHOR: 47a8059b
-    </activity>  # DIFF-ANCHOR: a208e726
-    <activity  # DIFF-ANCHOR: e0427380
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge">
-    </activity>  # DIFF-ANCHOR: e0427380
-    <activity  # DIFF-ANCHOR: b66ce3f2
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/edit_bookmark"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateHidden">
-    </activity>  # DIFF-ANCHOR: b66ce3f2
-    <activity  # DIFF-ANCHOR: 0a21ad35
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/bookmark_choose_folder"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateAlwaysHidden">
-    </activity>  # DIFF-ANCHOR: 0a21ad35
     <activity  # DIFF-ANCHOR: be2dce69
         android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
         android:excludeFromRecents="true"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index 8c415e3..dac7160 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -160,6 +160,35 @@
         android:taskAffinity=""
         android:theme="@style/Theme.BrowserUI.NoDisplay">
     </activity>  # DIFF-ANCHOR: 76686af9
+    <activity  # DIFF-ANCHOR: dc170161
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
+    </activity>  # DIFF-ANCHOR: dc170161
+    <activity  # DIFF-ANCHOR: d8508de6
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge">
+    </activity>  # DIFF-ANCHOR: d8508de6
+    <activity  # DIFF-ANCHOR: 766e66bd
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/edit_bookmark"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateHidden">
+    </activity>  # DIFF-ANCHOR: 766e66bd
+    <activity  # DIFF-ANCHOR: 8f8d0c81
+        android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/bookmark_choose_folder"
+        android:theme="@style/Theme.Chromium.DialogWhenLarge"
+        android:windowSoftInputMode="stateAlwaysHidden">
+    </activity>  # DIFF-ANCHOR: 8f8d0c81
     <activity  # DIFF-ANCHOR: 8f3f76d9
         android:name="org.chromium.chrome.browser.app.download.home.DownloadActivity"
         android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
@@ -205,47 +234,6 @@
         android:exported="false"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
     </activity>  # DIFF-ANCHOR: 170e9f21
-    <activity  # DIFF-ANCHOR: da2eedc8
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.Activity.Fullscreen"
-        android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
-    </activity>  # DIFF-ANCHOR: da2eedc8
-    <activity  # DIFF-ANCHOR: a208e726
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:excludeFromRecents="true"
-        android:exported="true"
-        android:theme="@style/Theme.BrowserUI.NoDisplay"
-        android:windowSoftInputMode="stateHidden">
-      <intent-filter>  # DIFF-ANCHOR: 47a8059b
-        <action android:name="$PACKAGE.ADDBOOKMARK"/>
-        <category android:name="android.intent.category.DEFAULT"/>
-      </intent-filter>  # DIFF-ANCHOR: 47a8059b
-    </activity>  # DIFF-ANCHOR: a208e726
-    <activity  # DIFF-ANCHOR: e0427380
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge">
-    </activity>  # DIFF-ANCHOR: e0427380
-    <activity  # DIFF-ANCHOR: b66ce3f2
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/edit_bookmark"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateHidden">
-    </activity>  # DIFF-ANCHOR: b66ce3f2
-    <activity  # DIFF-ANCHOR: 0a21ad35
-        android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
-        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-        android:exported="false"
-        android:label="@string/bookmark_choose_folder"
-        android:theme="@style/Theme.Chromium.DialogWhenLarge"
-        android:windowSoftInputMode="stateAlwaysHidden">
-    </activity>  # DIFF-ANCHOR: 0a21ad35
     <activity  # DIFF-ANCHOR: be2dce69
         android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
         android:excludeFromRecents="true"
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 3bcbcdb..f333b2c4 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -588,36 +588,27 @@
             android:label="@string/storage_management_activity_label"
             android:exported="false">
         </activity>
-        <activity android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
+
+        <!-- Activities for bookmarks. -->
+        <activity android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkActivity"
             android:theme="@style/Theme.Chromium.Activity.Fullscreen"
             android:exported="false"
             android:windowSoftInputMode="stateAlwaysHidden|adjustResize"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
         </activity>
-        <activity android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
-            android:theme="@style/Theme.BrowserUI.NoDisplay"
-            android:excludeFromRecents="true"
-            android:windowSoftInputMode="stateHidden"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="{{ manifest_package }}.ADDBOOKMARK" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-        <activity android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
+        <activity android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity"
             android:theme="@style/Theme.Chromium.DialogWhenLarge"
             android:windowSoftInputMode="stateHidden"
             android:exported="false"
             android:label="@string/edit_bookmark"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
         </activity>
-        <activity android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
+        <activity android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity"
             android:theme="@style/Theme.Chromium.DialogWhenLarge"
             android:exported="false"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
         </activity>
-        <activity android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
+        <activity android:name="org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity"
             android:theme="@style/Theme.Chromium.DialogWhenLarge"
             android:windowSoftInputMode="stateAlwaysHidden"
             android:label="@string/bookmark_choose_folder"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index e11e85b..14f96e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -183,6 +183,7 @@
 import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer;
 import org.chromium.chrome.browser.translate.TranslateIntentHandler;
 import org.chromium.chrome.browser.ui.AppLaunchDrawBlocker;
+import org.chromium.chrome.browser.ui.IncognitoRestoreAppLaunchDrawBlockerFactory;
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
@@ -361,6 +362,9 @@
     private final UnownedUserDataSupplier<StartupPaintPreviewHelper>
             mStartupPaintPreviewHelperSupplier = new StartupPaintPreviewHelperSupplier();
 
+    /** An empty {@link Bundle} would be supplied in the event there was no saved instance state. */
+    private final OneshotSupplierImpl<Bundle> mSavedInstanceStateSupplier =
+            new OneshotSupplierImpl<>();
     private final OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderSupplier =
             new OneshotSupplierImpl<>();
 
@@ -448,7 +452,9 @@
         mAppLaunchDrawBlocker = new AppLaunchDrawBlocker(getLifecycleDispatcher(),
                 () -> findViewById(android.R.id.content),
                 this::getIntent, this::shouldIgnoreIntent, this::isTablet,
-                this::shouldShowOverviewPageOnStart, this::isInstantStartEnabled);
+                this::shouldShowOverviewPageOnStart, this::isInstantStartEnabled,
+                new IncognitoRestoreAppLaunchDrawBlockerFactory(mSavedInstanceStateSupplier,
+                        getTabModelSelectorSupplier()));
         // clang-format on
     }
 
@@ -1237,6 +1243,10 @@
             super.initializeState();
             Log.i(TAG, "#initializeState");
             Intent intent = getIntent();
+            // Don't remove this line as this is used to unblock the app draw. Setting this
+            // supplier is very important.
+            mSavedInstanceStateSupplier.set(
+                    getSavedInstanceState() != null ? getSavedInstanceState() : new Bundle());
 
             boolean hadCipherData =
                     CipherFactory.getInstance().restoreFromBundle(getSavedInstanceState());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
index 1397f71..d23fd0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SynchronousInitializationActivity.java
@@ -8,7 +8,7 @@
 
 import androidx.annotation.CallSuper;
 
-import org.chromium.chrome.browser.bookmarks.BookmarkActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 298bbea..22d0aa5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -722,6 +722,8 @@
      * This function implements the actual layout inflation, Subclassing Activities that override
      * this method without calling super need to call {@link #onInitialLayoutInflationComplete()}.
      */
+    // TODO(crbug.com/1336778): Remove the @SuppressLint.
+    @SuppressLint("MissingInflatedId")
     protected void doLayoutInflation() {
         try (TraceEvent te = TraceEvent.scoped("ChromeActivity.doLayoutInflation")) {
             // Allow disk access for the content view and toolbar container setup.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActivity.java
rename to chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
index 4b1e8ad..f007e3c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import android.content.Intent;
 import android.os.Bundle;
@@ -14,6 +14,8 @@
 import org.chromium.chrome.browser.BackPressHelper;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.SnackbarActivity;
+import org.chromium.chrome.browser.bookmarks.BookmarkManager;
+import org.chromium.chrome.browser.bookmarks.BookmarkPage;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.embedder_support.util.UrlConstants;
 
@@ -23,9 +25,8 @@
  * tablet the bookmark UI is shown inside of a tab (see {@link BookmarkPage}).
  */
 public class BookmarkActivity extends SnackbarActivity {
-
     private BookmarkManager mBookmarkManager;
-    static final int EDIT_BOOKMARK_REQUEST_CODE = 14;
+    public static final int EDIT_BOOKMARK_REQUEST_CODE = 14;
     public static final String INTENT_VISIT_BOOKMARK_ID = "BookmarkEditActivity.VisitBookmarkId";
 
     @Override
@@ -54,8 +55,8 @@
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         if (requestCode == EDIT_BOOKMARK_REQUEST_CODE && resultCode == RESULT_OK) {
-            BookmarkId bookmarkId = BookmarkId.getBookmarkIdFromString(data.getStringExtra(
-                    INTENT_VISIT_BOOKMARK_ID));
+            BookmarkId bookmarkId = BookmarkId.getBookmarkIdFromString(
+                    data.getStringExtra(INTENT_VISIT_BOOKMARK_ID));
             mBookmarkManager.openBookmark(bookmarkId);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
similarity index 89%
rename from chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
rename to chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
index 17325f8..4952eab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddEditFolderActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import android.content.Context;
 import android.content.Intent;
@@ -22,6 +22,8 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.bookmarks.BookmarkTextInputLayout;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 
@@ -33,12 +35,11 @@
  * mode and editing mode. Depending on different modes, it should be started via two static creator
  * functions.
  */
-public class BookmarkAddEditFolderActivity extends SynchronousInitializationActivity
-        implements OnClickListener {
+public class BookmarkAddEditFolderActivity
+        extends SynchronousInitializationActivity implements OnClickListener {
     static final String INTENT_IS_ADD_MODE = "BookmarkAddEditFolderActivity.isAddMode";
     static final String INTENT_BOOKMARK_ID = "BookmarkAddEditFolderActivity.BookmarkId";
-    static final String
-            INTENT_CREATED_BOOKMARK = "BookmarkAddEditFolderActivity.createdBookmark";
+    static final String INTENT_CREATED_BOOKMARK = "BookmarkAddEditFolderActivity.createdBookmark";
     static final int PARENT_FOLDER_REQUEST_CODE = 10;
 
     private boolean mIsAddMode;
@@ -59,8 +60,11 @@
         @Override
         public void bookmarkModelChanged() {
             if (mIsAddMode) {
-                if (mModel.doesBookmarkExist(mParentId)) updateParent(mParentId);
-                else updateParent(mModel.getDefaultFolder());
+                if (mModel.doesBookmarkExist(mParentId)) {
+                    updateParent(mParentId);
+                } else {
+                    updateParent(mModel.getDefaultFolder());
+                }
             } else {
                 // Partner bookmark deletion is notified via bookmarkModelChanged().
                 if (mModel.doesBookmarkExist(mFolderId)) {
@@ -72,8 +76,8 @@
         }
 
         @Override
-        public void bookmarkNodeMoved(BookmarkItem oldParent, int oldIndex, BookmarkItem newParent,
-                int newIndex) {
+        public void bookmarkNodeMoved(
+                BookmarkItem oldParent, int oldIndex, BookmarkItem newParent, int newIndex) {
             if (!oldParent.getId().equals(newParent.getId())
                     && mModel.getChildAt(newParent.getId(), newIndex).equals(mFolderId)) {
                 updateParent(newParent.getId());
@@ -103,8 +107,8 @@
      * Starts an add folder activity. This method should only be called by
      * {@link BookmarkFolderSelectActivity}.
      */
-    public static void startAddFolderActivity(BookmarkFolderSelectActivity activity,
-            List<BookmarkId> bookmarksToMove) {
+    public static void startAddFolderActivity(
+            BookmarkFolderSelectActivity activity, List<BookmarkId> bookmarksToMove) {
         assert bookmarksToMove.size() > 0;
         Intent intent = new Intent(activity, BookmarkAddEditFolderActivity.class);
         intent.putExtra(INTENT_IS_ADD_MODE, true);
@@ -114,8 +118,8 @@
         }
         intent.putStringArrayListExtra(
                 BookmarkFolderSelectActivity.INTENT_BOOKMARKS_TO_MOVE, bookmarkStrings);
-        activity.startActivityForResult(intent,
-                BookmarkFolderSelectActivity.CREATE_FOLDER_REQUEST_CODE);
+        activity.startActivityForResult(
+                intent, BookmarkFolderSelectActivity.CREATE_FOLDER_REQUEST_CODE);
     }
 
     @Override
@@ -174,8 +178,7 @@
         assert v == mParentTextView;
 
         if (mIsAddMode) {
-            BookmarkFolderSelectActivity.startNewFolderSelectActivity(
-                    this, mBookmarksToMove);
+            BookmarkFolderSelectActivity.startNewFolderSelectActivity(this, mBookmarksToMove);
         } else {
             BookmarkFolderSelectActivity.startFolderSelectActivity(this, mFolderId);
         }
@@ -245,8 +248,8 @@
         super.onActivityResult(requestCode, resultCode, data);
         assert mIsAddMode;
         if (requestCode == PARENT_FOLDER_REQUEST_CODE && resultCode == RESULT_OK) {
-            BookmarkId selectedBookmark = BookmarkId.getBookmarkIdFromString(data.getStringExtra(
-                    BookmarkFolderSelectActivity.INTENT_SELECTED_FOLDER));
+            BookmarkId selectedBookmark = BookmarkId.getBookmarkIdFromString(
+                    data.getStringExtra(BookmarkFolderSelectActivity.INTENT_SELECTED_FOLDER));
             updateParent(selectedBookmark);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditActivity.java
similarity index 94%
rename from chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
rename to chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditActivity.java
index edf7b6c..f654204 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkEditActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditActivity.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import android.content.Intent;
 import android.os.Bundle;
@@ -19,6 +19,8 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.bookmarks.BookmarkTextInputLayout;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.url_formatter.UrlFormatter;
@@ -62,8 +64,8 @@
         super.onCreate(savedInstanceState);
 
         mModel = new BookmarkModel();
-        mBookmarkId = BookmarkId.getBookmarkIdFromString(
-                getIntent().getStringExtra(INTENT_BOOKMARK_ID));
+        mBookmarkId =
+                BookmarkId.getBookmarkIdFromString(getIntent().getStringExtra(INTENT_BOOKMARK_ID));
         mModel.addObserver(mBookmarkModelObserver);
         BookmarkItem item = mModel.getBookmarkById(mBookmarkId);
         if (!mModel.doesBookmarkExist(mBookmarkId) || item == null) {
@@ -162,8 +164,7 @@
                 mModel.setBookmarkTitle(mBookmarkId, title);
             }
 
-            if (!mUrlEditText.isEmpty()
-                    && mModel.getBookmarkById(mBookmarkId).isUrlEditable()) {
+            if (!mUrlEditText.isEmpty() && mModel.getBookmarkById(mBookmarkId).isUrlEditable()) {
                 GURL fixedUrl = UrlFormatter.fixupUrl(url);
                 if (fixedUrl.isValid() && !fixedUrl.equals(originalUrl)) {
                     mModel.setBookmarkUrl(mBookmarkId, fixedUrl);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderSelectActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
similarity index 92%
rename from chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderSelectActivity.java
rename to chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
index e0a7cef..7156890 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderSelectActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkFolderSelectActivity.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import android.app.Activity;
 import android.content.Context;
@@ -30,6 +30,9 @@
 import org.chromium.chrome.browser.SynchronousInitializationActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
+import org.chromium.chrome.browser.bookmarks.ReadingListFeatures;
 import org.chromium.chrome.browser.read_later.ReadingListUtils;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableItemView;
@@ -47,14 +50,11 @@
  * Note this fragment will not be restarted by OS. It will be dismissed if chrome is killed in
  * background.
  */
-public class BookmarkFolderSelectActivity extends SynchronousInitializationActivity implements
-        AdapterView.OnItemClickListener {
-    static final String
-            INTENT_SELECTED_FOLDER = "BookmarkFolderSelectActivity.selectedFolder";
-    static final String
-            INTENT_IS_CREATING_FOLDER = "BookmarkFolderSelectActivity.isCreatingFolder";
-    static final String
-            INTENT_BOOKMARKS_TO_MOVE = "BookmarkFolderSelectActivity.bookmarksToMove";
+public class BookmarkFolderSelectActivity
+        extends SynchronousInitializationActivity implements AdapterView.OnItemClickListener {
+    static final String INTENT_SELECTED_FOLDER = "BookmarkFolderSelectActivity.selectedFolder";
+    static final String INTENT_IS_CREATING_FOLDER = "BookmarkFolderSelectActivity.isCreatingFolder";
+    static final String INTENT_BOOKMARKS_TO_MOVE = "BookmarkFolderSelectActivity.bookmarksToMove";
     static final String INTENT_BOOKMARK_MOVE_RESULT =
             "BookmarkFolderSelectActivity.bookmarkMoveResult";
     static final int CREATE_FOLDER_REQUEST_CODE = 13;
@@ -130,8 +130,8 @@
         bookmarks.toArray(bookmarksArray);
         Intent intent = BookmarkFolderSelectActivity.createIntent(
                 activity, /*createFolder=*/true, bookmarksArray);
-        activity.startActivityForResult(intent,
-                BookmarkAddEditFolderActivity.PARENT_FOLDER_REQUEST_CODE);
+        activity.startActivityForResult(
+                intent, BookmarkAddEditFolderActivity.PARENT_FOLDER_REQUEST_CODE);
     }
 
     @Override
@@ -170,8 +170,7 @@
         if (mIsCreatingFolder) {
             mParentId = mModel.getMobileFolderId();
         } else {
-            mParentId = mModel.getBookmarkById(mBookmarksToMove.get(0))
-                    .getParentId();
+            mParentId = mModel.getBookmarkById(mBookmarksToMove.get(0)).getParentId();
         }
 
         setContentView(R.layout.bookmark_folder_select_activity);
@@ -209,9 +208,8 @@
         List<FolderListEntry> entryList = new ArrayList<>(folderList.size() + 3);
 
         if (!mIsCreatingFolder) {
-            entryList.add(new FolderListEntry(null, 0,
-                    getString(R.string.bookmark_add_folder), false,
-                    FolderListEntry.TYPE_NEW_FOLDER));
+            entryList.add(new FolderListEntry(null, 0, getString(R.string.bookmark_add_folder),
+                    false, FolderListEntry.TYPE_NEW_FOLDER));
         }
 
         FolderListEntry scrollToEntry = null;
@@ -323,8 +321,8 @@
         boolean mIsSelected;
         int mType;
 
-        public FolderListEntry(BookmarkId bookmarkId, int depth, String title, boolean isSelected,
-                int type) {
+        public FolderListEntry(
+                BookmarkId bookmarkId, int depth, String title, boolean isSelected, int type) {
             assert type == TYPE_NEW_FOLDER || type == TYPE_NORMAL;
             mDepth = depth;
             mId = bookmarkId;
@@ -345,8 +343,8 @@
         List<FolderListEntry> mEntryList = new ArrayList<>();
 
         public FolderListAdapter(Context context) {
-            mBasePadding = context.getResources()
-                    .getDimensionPixelSize(R.dimen.bookmark_folder_item_left);
+            mBasePadding =
+                    context.getResources().getDimensionPixelSize(R.dimen.bookmark_folder_item_left);
             mPaddingIncrement = mBasePadding * 2;
         }
 
@@ -434,8 +432,8 @@
          * Sets up padding for the entry
          */
         private void setUpPadding(FolderListEntry entry, View view) {
-            int paddingStart = mBasePadding + Math.min(entry.mDepth, MAX_FOLDER_DEPTH)
-                    * mPaddingIncrement;
+            int paddingStart =
+                    mBasePadding + Math.min(entry.mDepth, MAX_FOLDER_DEPTH) * mPaddingIncrement;
             ViewCompat.setPaddingRelative(view, paddingStart, view.getPaddingTop(), mBasePadding,
                     view.getPaddingBottom());
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
index 7412777..d6c778b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkActionBar.java
@@ -15,6 +15,8 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.tab.TabLaunchType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddActivity.java
deleted file mode 100644
index 03b6054..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 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.
-
-package org.chromium.chrome.browser.bookmarks;
-
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.chromium.base.Log;
-import org.chromium.chrome.R;
-import org.chromium.ui.widget.Toast;
-
-/**
- * This feature is no longer supported. TODO(twellington): remove this entirely Android L is no
- * longer supported.
- */
-public class BookmarkAddActivity extends AppCompatActivity {
-    private static final String TAG = "BookmarkAddActivity";
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(TAG, "This feature is no longer supported");
-        Toast.makeText(this, R.string.unsupported, Toast.LENGTH_SHORT).show();
-        finish();
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index 3e71a2d..2db13da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -300,7 +300,7 @@
         }
 
         // TODO(https://crbug.com/1019217): Remove when BookmarkModel is stubbed in tests instead.
-        void forceEditableForTesting() {
+        public void forceEditableForTesting() {
             mForceEditableForTesting = true;
         }
     }
@@ -1133,8 +1133,7 @@
         return BookmarkBridgeJni.get().isBookmarked(mNativeBookmarkBridge, url);
     }
 
-    @VisibleForTesting
-    BookmarkId getPartnerFolderId() {
+    public BookmarkId getPartnerFolderId() {
         ThreadUtils.assertOnUiThread();
         assert mIsNativeBookmarkModelLoaded;
         return BookmarkBridgeJni.get().getPartnerFolderId(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java
index b6c838a..7dd282af3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java
@@ -14,8 +14,7 @@
  * Interface used by UI components in the main bookmarks UI to broadcast UI change notifications
  * and get bookmark data model.
  */
-interface BookmarkDelegate {
-
+public interface BookmarkDelegate {
     /**
      * Delegate used to open urls for main fragment on tablet.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
index dcd750a..ef3ef78 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
@@ -54,7 +54,7 @@
 /**
  * BaseAdapter for {@link RecyclerView}. It manages bookmarks to list there.
  */
-class BookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkListEntry>
+public class BookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkListEntry>
         implements BookmarkUIObserver, SyncService.SyncStateChangedListener {
     private static final int MAXIMUM_NUMBER_OF_SEARCH_RESULTS = 500;
     private static final String EMPTY_QUERY = null;
@@ -433,7 +433,7 @@
     /**
      * Refresh the list of bookmarks within the currently visible folder.
      */
-    void refresh() {
+    public void refresh() {
         // Tell the RecyclerView to update its elements.
         if (mElements != null) notifyDataSetChanged();
     }
@@ -443,7 +443,7 @@
      *
      * @param query The query text to search for.
      */
-    void search(String query) {
+    public void search(String query) {
         mSearchText = query.trim();
         List<BookmarkId> result =
                 mDelegate.getModel().searchBookmarks(mSearchText, MAXIMUM_NUMBER_OF_SEARCH_RESULTS);
@@ -615,8 +615,7 @@
         return isReorderable(getItemByHolder(viewHolder));
     }
 
-    @VisibleForTesting
-    BookmarkId getIdByPosition(int position) {
+    public BookmarkId getIdByPosition(int position) {
         BookmarkListEntry entry = getItemByPosition(position);
         if (entry == null || entry.getBookmarkItem() == null) return null;
         return entry.getBookmarkItem().getId();
@@ -647,7 +646,7 @@
     }
 
     @VisibleForTesting
-    void simulateSignInForTests() {
+    public void simulateSignInForTests() {
         syncStateChanged();
         onFolderStateSet(mCurrentFolder);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java
index 8640db10..9f04e0c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java
@@ -21,7 +21,7 @@
 /**
  * Represents different type of views in the bookmark UI.
  */
-final class BookmarkListEntry {
+public final class BookmarkListEntry {
     /**
      * Specifies the view types that the bookmark delegate screen can contain.
      */
@@ -29,7 +29,7 @@
     @IntDef({ViewType.INVALID, ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.PERSONALIZED_SYNC_PROMO,
             ViewType.SYNC_PROMO, ViewType.FOLDER, ViewType.BOOKMARK, ViewType.DIVIDER,
             ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST})
-    @interface ViewType {
+    public @interface ViewType {
         int INVALID = -1;
         int PERSONALIZED_SIGNIN_PROMO = 0;
         int PERSONALIZED_SYNC_PROMO = 1;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 2194b04..314fad3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -21,6 +21,7 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
 import org.chromium.chrome.browser.commerce.shopping_list.ShoppingFeatures;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
index 5c28a17a..4f00f88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
@@ -99,7 +99,7 @@
      * Calls {@link BookmarkBridge#moveBookmark(BookmarkId, BookmarkId, int)} for the given
      * bookmark list. The bookmarks are appended at the end.
      */
-    void moveBookmarks(List<BookmarkId> bookmarkIds, BookmarkId newParentId) {
+    public void moveBookmarks(List<BookmarkId> bookmarkIds, BookmarkId newParentId) {
         int appendIndex = getChildCount(newParentId);
         for (int i = 0; i < bookmarkIds.size(); ++i) {
             moveBookmark(bookmarkIds.get(i), newParentId, appendIndex + i);
@@ -109,7 +109,7 @@
     /**
      * @see org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem#getTitle()
      */
-    String getBookmarkTitle(BookmarkId bookmarkId) {
+    public String getBookmarkTitle(BookmarkId bookmarkId) {
         BookmarkItem bookmarkItem = getBookmarkById(bookmarkId);
         if (bookmarkItem == null) return "";
         return bookmarkItem.getTitle();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java
index a31a483..7880f296 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPromoHeader.java
@@ -38,8 +38,9 @@
  * Class that manages all the logic and UI behind the signin promo header in the bookmark
  * content UI. The header is shown only on certain situations, (e.g., not signed in).
  */
-class BookmarkPromoHeader implements SyncService.SyncStateChangedListener, SignInStateObserver,
-                                     ProfileDataCache.Observer, AccountsChangeObserver {
+public class BookmarkPromoHeader implements SyncService.SyncStateChangedListener,
+                                            SignInStateObserver, ProfileDataCache.Observer,
+                                            AccountsChangeObserver {
     // TODO(kkimlabs): Figure out the optimal number based on UMA data.
     private static final int MAX_SIGNIN_AND_SYNC_PROMO_SHOW_COUNT = 10;
 
@@ -254,7 +255,7 @@
      * @param promoState The promo state to which the header will be set to.
      */
     @VisibleForTesting
-    static void forcePromoStateForTests(@Nullable @SyncPromoState Integer promoState) {
+    public static void forcePromoStateForTests(@Nullable @SyncPromoState Integer promoState) {
         sPromoStateForTests = promoState;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
index 20c7b1f..755919f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -19,6 +19,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
@@ -321,8 +323,7 @@
         mDragHandle.setOnTouchListener(l);
     }
 
-    @VisibleForTesting
-    String getTitle() {
+    public String getTitle() {
         return String.valueOf(mTitleView.getText());
     }
 
@@ -351,7 +352,7 @@
     }
 
     @VisibleForTesting
-    View getDragHandleViewForTests() {
+    public View getDragHandleViewForTests() {
         return mDragHandle;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
index 437c2e0..8072a54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
@@ -56,14 +56,14 @@
     /**
      * @return Trimmed text for validation.
      */
-    String getTrimmedText() {
+    public String getTrimmedText() {
         return getEditText().getText().toString().trim();
     }
 
     /**
      * @return Whether the content is empty.
      */
-    boolean isEmpty() {
+    public boolean isEmpty() {
         return TextUtils.isEmpty(getTrimmedText());
     }
 
@@ -72,7 +72,7 @@
      * If there is a need for extra validation, this method should be overridden
      * and extra validation statements should be added after calling super.validate()
      */
-    void validate() {
+    public void validate() {
         if (mEmptyErrorMessage != null) {
             setError(isEmpty() ? mEmptyErrorMessage : null);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUIState.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUIState.java
index c84cdd5..1e17ee9f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUIState.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUIState.java
@@ -14,10 +14,10 @@
  * A class representing the UI state of the {@link BookmarkManager}. All
  * states can be uniquely identified by a URL.
  */
-class BookmarkUIState {
-    static final int STATE_LOADING = 1;
-    static final int STATE_FOLDER = 2;
-    static final int STATE_SEARCHING = 3;
+public class BookmarkUIState {
+    public static final int STATE_LOADING = 1;
+    public static final int STATE_FOLDER = 2;
+    public static final int STATE_SEARCHING = 3;
     private static final int STATE_INVALID = 0;
     private static final String SHOPPING_FILTER_URL =
             UrlConstants.BOOKMARKS_FOLDER_URL + "/shopping";
@@ -91,7 +91,7 @@
         return state;
     }
 
-    static Uri createFolderUrl(BookmarkId folderId) {
+    public static Uri createFolderUrl(BookmarkId folderId) {
         Uri.Builder builder = Uri.parse(UrlConstants.BOOKMARKS_FOLDER_URL).buildUpon();
         // Encodes the path and appends it to the base url. A simple appending
         // does not work because there might be spaces in suffix.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index 3e0d28a..a404659 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -34,6 +34,9 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.IntentHandler.IncognitoCCTCallerId;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType;
 import org.chromium.chrome.browser.commerce.shopping_list.ShoppingFeatures;
@@ -387,7 +390,7 @@
      * Fetches url representing the user's state last time they close the bookmark manager.
      */
     @VisibleForTesting
-    static String getLastUsedUrl(Context context) {
+    public static String getLastUsedUrl(Context context) {
         return SharedPreferencesManager.getInstance().readString(
                 ChromePreferenceKeys.BOOKMARKS_LAST_USED_URL, UrlConstants.BOOKMARKS_URL);
     }
@@ -395,7 +398,7 @@
     /**
      * Save the last used {@link BookmarkId} as a folder to put new bookmarks to.
      */
-    static void setLastUsedParent(Context context, BookmarkId bookmarkId) {
+    public static void setLastUsedParent(Context context, BookmarkId bookmarkId) {
         SharedPreferencesManager.getInstance().writeString(
                 ChromePreferenceKeys.BOOKMARKS_LAST_USED_PARENT, bookmarkId.toString());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
index 15d0e30b..d61bc81 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
@@ -208,9 +208,14 @@
                 commandArgs.putString(ARG_NOTIFICATION_CHANNEL_NAME, channelName);
                 Bundle commandResult = service.sendExtraCommand(
                         COMMAND_CHECK_NOTIFICATION_PERMISSION, commandArgs, /*callback=*/null);
+                boolean commandSuccess = commandResult == null
+                        ? false
+                        : commandResult.getBoolean(EXTRA_COMMAND_SUCCESS);
+                mRecorder.recordExtraCommandSuccess(
+                        COMMAND_CHECK_NOTIFICATION_PERMISSION, commandSuccess);
                 // The command might fail if the app is too old to support it. To handle that case,
                 // fall back to the old flow.
-                if (commandResult == null || !commandResult.getBoolean(EXTRA_COMMAND_SUCCESS)) {
+                if (!commandSuccess) {
                     boolean enabled = service.areNotificationsEnabled(channelName);
                     @ContentSettingValues
                     int settingValue =
@@ -255,12 +260,17 @@
                 Bundle commandResult = service.sendExtraCommand(
                         COMMAND_GET_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT, commandArgs,
                         /*callback=*/null);
-                PendingIntent pendingIntent = commandResult.getParcelable(
-                        KEY_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT);
-                // TODO(crbug.com/1320272) UMA logging for these results. Is defaulting to BLOCK a
-                // good enough way to handle outdated TWAs?
-                if (commandResult == null || !commandResult.getBoolean(EXTRA_COMMAND_SUCCESS)
-                        || pendingIntent == null) {
+                boolean commandSuccess = commandResult == null
+                        ? false
+                        : commandResult.getBoolean(EXTRA_COMMAND_SUCCESS);
+                PendingIntent pendingIntent = commandSuccess
+                        ? commandResult.getParcelable(
+                                KEY_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT)
+                        : null;
+                mRecorder.recordExtraCommandSuccess(
+                        COMMAND_GET_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT,
+                        commandSuccess && pendingIntent != null);
+                if (!commandSuccess || pendingIntent == null) {
                     permissionCallback.onPermission(
                             service.getComponentName(), ContentSettingValues.BLOCK);
                     return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdater.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdater.java
index 7b584935..7285fa5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdater.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdater.java
@@ -16,6 +16,8 @@
 import org.chromium.base.Log;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
+import org.chromium.chrome.browser.browserservices.metrics.TrustedWebActivityUmaRecorder;
+import org.chromium.chrome.browser.browserservices.metrics.WebApkUmaRecorder;
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
@@ -146,6 +148,8 @@
                             ComponentName app, @ContentSettingValues int settingValue) {
                         if (mCalled) return;
                         mCalled = true;
+                        TrustedWebActivityUmaRecorder.recordNotificationPermissionRequestResult(
+                                settingValue);
                         updatePermission(origin, callback, app.getPackageName(), settingValue);
                     }
 
@@ -168,8 +172,11 @@
             return;
         }
 
-        WebApkServiceClient.getInstance().requestNotificationPermission(packageName,
-                settingValue -> updatePermission(origin, callback, packageName, settingValue));
+        WebApkServiceClient.getInstance().requestNotificationPermission(
+                packageName, settingValue -> {
+                    WebApkUmaRecorder.recordNotificationPermissionRequestResult(settingValue);
+                    updatePermission(origin, callback, packageName, settingValue);
+                });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlocker.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlocker.java
index a1f6b14..06e9271b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlocker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlocker.java
@@ -76,11 +76,18 @@
     private final Supplier<Boolean> mIsInstantStartEnabledSupplier;
 
     /**
+     * An app draw blocker that takes care of blocking the draw when we are restoring tabs with
+     * Incognito.
+     */
+    private final IncognitoRestoreAppLaunchDrawBlocker mIncognitoRestoreAppLaunchDrawBlocker;
+
+    /**
      * Whether to return false from #onPreDraw of the content view to prevent drawing the browser UI
      * before the tab is ready.
      */
     private boolean mBlockDrawForInitialTab;
     private boolean mBlockDrawForOverviewPage;
+    private boolean mBlockDrawForIncognitoRestore;
     private long mTimeStartedBlockingDrawForInitialTab;
 
     /**
@@ -94,13 +101,17 @@
      * @param isTabletSupplier {@link Supplier<Boolean>} for whether the device is a tablet.
      * @param shouldShowTabSwitcherOnStartSupplier {@link Supplier<Boolean>} for whether the tab
      *        switcher should be shown on start.
+     * @param incognitoRestoreAppLaunchDrawBlockerFactory Factory to create
+     *    {@link IncognitoRestoreAppLaunchDrawBlocker}.
      */
     public AppLaunchDrawBlocker(@NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
             @NonNull Supplier<View> viewSupplier, @NonNull Supplier<Intent> intentSupplier,
             @NonNull Supplier<Boolean> shouldIgnoreIntentSupplier,
             @NonNull Supplier<Boolean> isTabletSupplier,
             @NonNull Supplier<Boolean> shouldShowTabSwitcherOnStartSupplier,
-            @NonNull Supplier<Boolean> isInstantStartEnabledSupplier) {
+            @NonNull Supplier<Boolean> isInstantStartEnabledSupplier,
+            @NonNull IncognitoRestoreAppLaunchDrawBlockerFactory
+                    incognitoRestoreAppLaunchDrawBlockerFactory) {
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mViewSupplier = viewSupplier;
         mInflationObserver = new InflationObserver() {
@@ -110,6 +121,7 @@
             @Override
             public void onPostInflationStartup() {
                 maybeBlockDraw();
+                maybeBlockDrawForIncognitoRestore();
             }
         };
         mActivityLifecycleDispatcher.register(mInflationObserver);
@@ -128,12 +140,16 @@
         mIsTabletSupplier = isTabletSupplier;
         mShouldShowOverviewPageOnStartSupplier = shouldShowTabSwitcherOnStartSupplier;
         mIsInstantStartEnabledSupplier = isInstantStartEnabledSupplier;
+        mIncognitoRestoreAppLaunchDrawBlocker = incognitoRestoreAppLaunchDrawBlockerFactory.create(
+                intentSupplier, shouldIgnoreIntentSupplier, activityLifecycleDispatcher,
+                this::onIncognitoRestoreUnblockConditionsFired);
     }
 
     /** Unregister lifecycle observers. */
     public void destroy() {
         mActivityLifecycleDispatcher.unregister(mInflationObserver);
         mActivityLifecycleDispatcher.unregister(mStartStopWithNativeObserver);
+        mIncognitoRestoreAppLaunchDrawBlocker.destroy();
     }
 
     /** Should be called when the initial tab is available. */
@@ -156,6 +172,20 @@
         mBlockDrawForOverviewPage = false;
     }
 
+    /**
+     * A method that is passed as a {@link Runnable} to {@link
+     * IncognitoRestoreAppLaunchDrawBlocker}.
+     *
+     * This gets fired when all the conditions needed to unblock the draw from the Incognito restore
+     * are fired.
+     */
+    private void onIncognitoRestoreUnblockConditionsFired() {
+        if (mBlockDrawForIncognitoRestore) {
+            recordBlockDrawForIncognitoRestoreHistograms();
+            mBlockDrawForIncognitoRestore = false;
+        }
+    }
+
     private void writeSearchEngineHadLogoPref() {
         boolean searchEngineHasLogo =
                 TemplateUrlServiceFactory.get().doesDefaultSearchEngineHaveLogo();
@@ -163,6 +193,17 @@
                 ChromePreferenceKeys.APP_LAUNCH_SEARCH_ENGINE_HAD_LOGO, searchEngineHasLogo);
     }
 
+    /**
+     * Conditionally blocks the draw independently from the other clients for the Incognito restore
+     * use-case.
+     */
+    private void maybeBlockDrawForIncognitoRestore() {
+        if (!mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw()) return;
+        mBlockDrawForIncognitoRestore = true;
+        ViewDrawBlocker.blockViewDrawUntilReady(
+                mViewSupplier.get(), () -> !mBlockDrawForIncognitoRestore);
+    }
+
     /** Only block the draw if we believe the initial tab will be the NTP. */
     private void maybeBlockDraw() {
         if (mShouldShowOverviewPageOnStartSupplier.get()) {
@@ -276,4 +317,17 @@
         RecordHistogram.recordEnumeratedHistogram(APP_LAUNCH_BLOCK_DRAW_ACCURACY_UMA, enumEntry,
                 BlockDrawForInitialTabAccuracy.COUNT);
     }
+
+    /**
+     * TODO(crbug.com/1227656): Add the histogram to record the time we blocked the draw.
+     */
+    private void recordBlockDrawForIncognitoRestoreHistograms() {}
+
+    /**
+     * A test only method. Don't use in production code.
+     */
+    @VisibleForTesting
+    public void setBlockDrawForIncognitoRestore(boolean blockDraw) {
+        mBlockDrawForIncognitoRestore = blockDraw;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlocker.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlocker.java
index bbea7b9..59352f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlocker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlocker.java
@@ -10,8 +10,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
+import org.chromium.base.CallbackController;
 import org.chromium.base.CommandLine;
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.crypto.CipherFactory;
@@ -21,6 +24,7 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 
 /**
  * A class that provides functionality to block the initial draw for the Incognito restore flow.
@@ -32,8 +36,8 @@
      */
     public static final String IS_INCOGNITO_SELECTED = "is_incognito_selected";
 
-    /** A {@link Bundle} containing the saved instance state after the Activity is restarted. */
-    private final @NonNull Bundle mSavedInstanceState;
+    /** A {@link OneshotSupplier<Bundle>} for the saved instance state supplier. */
+    private final @NonNull OneshotSupplier<Bundle> mSavedInstanceStateSupplier;
     /** A supplier of {@link TabModelSelector} instance.*/
     private final @NonNull ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     /**
@@ -46,6 +50,15 @@
      * launched from one.
      */
     private final @NonNull Supplier<Intent> mIntentSupplier;
+    /** A {@link Supplier<Boolean>} to indicate whether we should ignore the intent. */
+    private final @NonNull Supplier<Boolean> mShouldIgnoreIntentSupplier;
+    /**
+     * A {@link Runnable} to unblock the draw operation. This is fired when both native and tab
+     * state has been initialized.
+     */
+    private final @NonNull Runnable mUnblockDrawRunnable;
+    /** A boolean so we don't fire unblock draw runnable twice. */
+    private boolean mIsUnblockDrawRunnableInvoked;
 
     /**
      * An observer to listen for the onFinishNativeInitialization events. This signal works as a
@@ -55,35 +68,84 @@
         @Override
         public void onFinishNativeInitialization() {
             mIsNativeInitializationFinished = true;
+            maybeUnblockDraw();
         }
     };
+    /**
+     * A {@link TabModelSelectorObserver} which notifies about the event when the tab state is
+     * initialized. This is one of the signal along with the native initialization that we look for
+     * to unblock the draw.
+     */
+    private final @NonNull TabModelSelectorObserver mTabModelSelectorObserver =
+            new TabModelSelectorObserver() {
+                @Override
+                public void onTabStateInitialized() {
+                    maybeUnblockDraw();
+                }
+            };
 
+    /** A callback to add the |mTabModelSelectorObserver|. */
+    private final @NonNull Callback<TabModelSelector> mTabModelSelectorSupplierCallback =
+            (tabModelSelector) -> {
+        tabModelSelector.addObserver(mTabModelSelectorObserver);
+    };
+
+    /**
+     * The {@link CallbackController} meant to be used to get the saved instance state when it
+     * becomes available.
+     */
+    private final CallbackController mSavedInstanceCallbackController = new CallbackController();
     /**
      * A boolean to indicate when native has finished initialization as by then we would have
      * finished creating the {@link IncognitoReauthController}.
      */
     private boolean mIsNativeInitializationFinished;
+    /** A {@link Bundle} containing the saved instance state after the Activity is restarted. */
+    private Bundle mSavedInstanceState;
 
     /**
-     * @param savedInstanceState A {@link Bundle} instance which was persisted during
-     *         onSaveInstanceState that allows to look for signals on whether to block the draw or
-     *         not.
+     * @param savedInstanceStateSupplier A {@link OneshotSupplier<Bundle>} instance to pass in the
+     *                                   bundle that was persisted during onSaveInstanceState that
+     *                                   allows to look for signals on whether to block the draw or
+     *                                   not. An empty {@link Bundle} would be supplied in the event
+     *                                   there was no saved instance state.
      * @param tabModelSelectorSupplier A {@link ObservableSupplier<TabModelSelector>} that allows to
-     *         listen for onTabStateInitialized signals which is used a fallback to unblock draw.
+     *                                 listen for onTabStateInitialized signals which is used a
+     *                                 fallback to unblock draw.
      * @param intentSupplier The {@link Supplier<Intent>} which is passed when Chrome was launched
-     *         through Intent.
-     * @param activityLifecycleDispatcher The {@link ActivityLifecycleDispatcher} which would allow
-     *         to listen for onFinishNativeInitialization signal.
+     *                       through Intent.
+     * @param shouldIgnoreIntentSupplier A {@link Supplier<Boolean>} to indicate whether we need to
+     *                                   ignore the intent.
+     * @param activityLifecycleDispatcher A {@link ActivityLifecycleDispatcher} which would allow to
+     *                                   listen for onFinishNativeInitialization signal.
+     * @param unblockDrawRunnable A {@link Runnable} to unblock the draw operation.
      */
-    public IncognitoRestoreAppLaunchDrawBlocker(@NonNull Bundle savedInstanceState,
+    IncognitoRestoreAppLaunchDrawBlocker(
+            @NonNull OneshotSupplier<Bundle> savedInstanceStateSupplier,
             @NonNull ObservableSupplier<TabModelSelector> tabModelSelectorSupplier,
             @NonNull Supplier<Intent> intentSupplier,
-            @NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher) {
-        mSavedInstanceState = savedInstanceState;
+            @NonNull Supplier<Boolean> shouldIgnoreIntentSupplier,
+            @NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
+            @NonNull Runnable unblockDrawRunnable) {
+        mSavedInstanceStateSupplier = savedInstanceStateSupplier;
         mTabModelSelectorSupplier = tabModelSelectorSupplier;
         mIntentSupplier = intentSupplier;
+        mShouldIgnoreIntentSupplier = shouldIgnoreIntentSupplier;
         mActivityLifecycleDispatcher = activityLifecycleDispatcher;
+        mUnblockDrawRunnable = unblockDrawRunnable;
+
         mActivityLifecycleDispatcher.register(mNativeInitObserver);
+        mSavedInstanceStateSupplier.onAvailable(
+                mSavedInstanceCallbackController.makeCancelable(savedInstanceState -> {
+                    mSavedInstanceState = savedInstanceState;
+                    // Draw is currently blocked because |mSavedInstanceState| was null, so let's
+                    // check if having a non-empty saved instance state would unblock us.
+                    if (!shouldBlockDraw()) {
+                        mIsUnblockDrawRunnableInvoked = true;
+                        mUnblockDrawRunnable.run();
+                    }
+                }));
+        mTabModelSelectorSupplier.addObserver(mTabModelSelectorSupplierCallback);
     }
 
     /**
@@ -91,6 +153,11 @@
      */
     public void destroy() {
         mActivityLifecycleDispatcher.unregister(mNativeInitObserver);
+        mSavedInstanceCallbackController.destroy();
+        mTabModelSelectorSupplier.removeObserver(mTabModelSelectorSupplierCallback);
+        if (mTabModelSelectorSupplier.get() != null) {
+            mTabModelSelectorSupplier.get().removeObserver(mTabModelSelectorObserver);
+        }
     }
 
     /**
@@ -100,12 +167,18 @@
      * @return True, if we need to block the draw. False, otherwise.
      */
     public boolean shouldBlockDraw() {
-        // We can't test for UserPrefs here sine the native may not be initialized and we will
+        // We can't test for UserPrefs here since the native may not be initialized and we will
         // just wait for onTabStateInitialized to be triggered.
         if (!IncognitoReauthManager.isIncognitoReauthFeatureAvailable()) return false;
         if (CommandLine.getInstance().hasSwitch(ChromeSwitches.NO_RESTORE_STATE)) return false;
-        if (!CipherFactory.getInstance().restoreFromBundle(mSavedInstanceState)) return false;
 
+        // A valid saved instance state is needed here. If it's null it means it might not have been
+        // made available yet. Therefore, we need to block the draw. Once it's available, we will
+        // make call again to this method with a valid saved instance state and unblock if any of
+        // these conditions returns false.
+        if (mSavedInstanceState == null) return true;
+
+        if (!CipherFactory.getInstance().restoreFromBundle(mSavedInstanceState)) return false;
         boolean isReauthPending = mSavedInstanceState.getBoolean(
                 IncognitoReauthController.KEY_IS_INCOGNITO_REAUTH_PENDING, false);
         // There were no Incognito tabs before the Activity got destroyed. So we don't need to block
@@ -114,7 +187,8 @@
 
         boolean isLastSelectedModelIncognito =
                 mSavedInstanceState.getBoolean(IS_INCOGNITO_SELECTED, false);
-        boolean isIncognitoFiredFromLauncherShortcut = mIntentSupplier.get() != null
+        boolean isIncognitoFiredFromLauncherShortcut = !mShouldIgnoreIntentSupplier.get()
+                && mIntentSupplier.get() != null
                 && mIntentSupplier.get().getBooleanExtra(
                         IntentHandler.EXTRA_INVOKED_FROM_LAUNCH_NEW_INCOGNITO_TAB, false);
 
@@ -135,11 +209,22 @@
         return true;
     }
 
+    private void maybeUnblockDraw() {
+        if (!mTabModelSelectorSupplier.hasValue()) return;
+        if (!mTabModelSelectorSupplier.get().isTabStateInitialized()) return;
+        if (!mIsNativeInitializationFinished) return;
+        if (mIsUnblockDrawRunnableInvoked) return;
+
+        mIsUnblockDrawRunnableInvoked = true;
+        mUnblockDrawRunnable.run();
+    }
+
     /**
-     * A test-only method to toggle the initialization state of the native.
+     * Test-only method.
      */
     @VisibleForTesting
-    public void setIsNativeInitializationFinished(boolean initialized) {
-        mIsNativeInitializationFinished = initialized;
+    public void resetIsUnblockDrawRunnableInvokedForTesting() {
+        assert mIsUnblockDrawRunnableInvoked : "Must be set before resetting.";
+        mIsUnblockDrawRunnableInvoked = false;
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerFactory.java
new file mode 100644
index 0000000..bc0adb6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerFactory.java
@@ -0,0 +1,59 @@
+// 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.
+
+package org.chromium.chrome.browser.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.OneshotSupplier;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+
+/**
+ * A factory class to create {@link IncognitoRestoreAppLaunchDrawBlocker}.
+ */
+public class IncognitoRestoreAppLaunchDrawBlockerFactory {
+    private final @NonNull OneshotSupplier<Bundle> mSavedInstanceStateSupplier;
+    private final @NonNull ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
+
+    /**
+     * @param savedInstanceStateSupplier A {@link OneshotSupplier<Bundle>} instance to pass in the
+     *                                   bundle that was persisted during onSaveInstanceState that
+     *                                   allows to look for signals on whether to block the draw or
+     *                                   not. An empty {@link Bundle} would be supplied in the event
+     *                                   there was no saved instance state.
+     * @param tabModelSelectorSupplier A {@link ObservableSupplier<TabModelSelector>} that allows to
+     *                                 listen for onTabStateInitialized signals which is used a
+     *                                 fallback to unblock draw.
+     */
+    public IncognitoRestoreAppLaunchDrawBlockerFactory(
+            @NonNull OneshotSupplier<Bundle> savedInstanceStateSupplier,
+            @NonNull ObservableSupplier<TabModelSelector> tabModelSelectorSupplier) {
+        mSavedInstanceStateSupplier = savedInstanceStateSupplier;
+        mTabModelSelectorSupplier = tabModelSelectorSupplier;
+    }
+
+    /**
+     * @param intentSupplier The {@link Supplier<Intent>} which is passed when Chrome was launched
+     *                       through Intent.
+     * @param shouldIgnoreIntentSupplier A {@link Supplier<Boolean>} to indicate whether we need to
+     *                                   ignore the intent.
+     * @param activityLifecycleDispatcher A {@link ActivityLifecycleDispatcher} which would allow to
+     *                                   listen for onFinishNativeInitialization signal.
+     * @param unblockDrawRunnable A {@link Runnable} to unblock the draw operation.
+     */
+    IncognitoRestoreAppLaunchDrawBlocker create(@NonNull Supplier<Intent> intentSupplier,
+            @NonNull Supplier<Boolean> shouldIgnoreIntentSupplier,
+            @NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
+            @NonNull Runnable unblockDrawRunnable) {
+        return new IncognitoRestoreAppLaunchDrawBlocker(mSavedInstanceStateSupplier,
+                mTabModelSelectorSupplier, intentSupplier, shouldIgnoreIntentSupplier,
+                activityLifecycleDispatcher, unblockDrawRunnable);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkEditTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkEditTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditTest.java
index c63f70e..37d0fa03 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkEditTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkEditTest.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import android.app.Activity;
 import android.content.Context;
@@ -30,6 +30,8 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.bookmarks.BookmarkModelTest;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
similarity index 98%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
index 9458a34..7cfae9e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -2,7 +2,7 @@
 // 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.bookmarks;
+package org.chromium.chrome.browser.app.bookmarks;
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.Espresso.pressBack;
@@ -65,8 +65,24 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.app.metrics.LaunchCauseMetrics;
+import org.chromium.chrome.browser.bookmarks.BookmarkActionBar;
+import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.BookmarkDelegate;
+import org.chromium.chrome.browser.bookmarks.BookmarkFolderRow;
+import org.chromium.chrome.browser.bookmarks.BookmarkItemRow;
+import org.chromium.chrome.browser.bookmarks.BookmarkItemsAdapter;
+import org.chromium.chrome.browser.bookmarks.BookmarkListEntry;
+import org.chromium.chrome.browser.bookmarks.BookmarkManager;
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
+import org.chromium.chrome.browser.bookmarks.BookmarkPage;
+import org.chromium.chrome.browser.bookmarks.BookmarkPromoHeader;
+import org.chromium.chrome.browser.bookmarks.BookmarkRow;
+import org.chromium.chrome.browser.bookmarks.BookmarkUIState;
+import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
+import org.chromium.chrome.browser.bookmarks.PowerBookmarkShoppingItemRow;
+import org.chromium.chrome.browser.bookmarks.ReadingListFeatures;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
index 79d0454a..de718684 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedPromoRenderTest.java
@@ -20,10 +20,12 @@
 
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -46,6 +48,7 @@
  * Tests for the personalized signin promo on the Bookmarks page.
  */
 @RunWith(ParameterizedRunner.class)
+@Batch(Batch.PER_CLASS)
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @DisableIf.Device(type = {UiDisableIf.TABLET})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java
index 040608b..6b946db6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/MainIntentBehaviorMetricsIntegrationTest.java
@@ -29,8 +29,8 @@
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.app.download.home.DownloadActivity;
-import org.chromium.chrome.browser.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.history.HistoryActivity;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
index 11625af..7caa5bd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
@@ -8,6 +8,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -25,6 +27,7 @@
 import android.view.ViewTreeObserver.OnPreDrawListener;
 
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -97,6 +100,11 @@
     private Supplier<Boolean> mShouldShowTabSwitcherOnStartSupplier;
     @Mock
     private Supplier<Boolean> mIsInstantStartEnabledSupplier;
+    @Mock
+    private IncognitoRestoreAppLaunchDrawBlockerFactory
+            mIncognitoRestoreAppLaunchDrawBlockerFactoryMock;
+    @Mock
+    private IncognitoRestoreAppLaunchDrawBlocker mIncognitoRestoreAppLaunchDrawBlockerMock;
     @Captor
     private ArgumentCaptor<OnPreDrawListener> mOnPreDrawListenerArgumentCaptor;
     @Captor
@@ -119,9 +127,13 @@
         when(mIsTabletSupplier.get()).thenReturn(false);
         when(mShouldShowTabSwitcherOnStartSupplier.get()).thenReturn(false);
         when(mIsInstantStartEnabledSupplier.get()).thenReturn(false);
+        when(mIncognitoRestoreAppLaunchDrawBlockerFactoryMock.create(eq(mIntentSupplier),
+                     eq(mShouldIgnoreIntentSupplier), eq(mActivityLifecycleDispatcher), any()))
+                .thenReturn(mIncognitoRestoreAppLaunchDrawBlockerMock);
         mAppLaunchDrawBlocker = new AppLaunchDrawBlocker(mActivityLifecycleDispatcher,
                 mViewSupplier, mIntentSupplier, mShouldIgnoreIntentSupplier, mIsTabletSupplier,
-                mShouldShowTabSwitcherOnStartSupplier, mIsInstantStartEnabledSupplier);
+                mShouldShowTabSwitcherOnStartSupplier, mIsInstantStartEnabledSupplier,
+                mIncognitoRestoreAppLaunchDrawBlockerFactoryMock);
         validateConstructorAndCaptureObservers();
         ShadowRecordHistogram.reset();
         SystemClock.setCurrentTimeMillis(INITIAL_TIME);
@@ -373,6 +385,46 @@
         assertAccuracyHistogram(true, false);
     }
 
+    @Test
+    @SmallTest
+    public void testShouldBlockDrawForIncognitoRestore_AddsOnPreDrawListener() {
+        when(mIncognitoRestoreAppLaunchDrawBlockerMock.shouldBlockDraw()).thenReturn(true);
+        mInflationObserver.onPostInflationStartup();
+        verify(mViewTreeObserver, times(2))
+                .addOnPreDrawListener(mOnPreDrawListenerArgumentCaptor.capture());
+        verify(mIncognitoRestoreAppLaunchDrawBlockerMock, times(1)).shouldBlockDraw();
+    }
+
+    @Test
+    @SmallTest
+    public void testShouldNotBlockDrawForIncognitoRestore_DoesNotAddOnPreDrawListener() {
+        when(mIncognitoRestoreAppLaunchDrawBlockerMock.shouldBlockDraw()).thenReturn(false);
+        mInflationObserver.onPostInflationStartup();
+        verify(mViewTreeObserver, times(1))
+                .addOnPreDrawListener(mOnPreDrawListenerArgumentCaptor.capture());
+        verify(mIncognitoRestoreAppLaunchDrawBlockerMock, times(1)).shouldBlockDraw();
+    }
+
+    @Test
+    @SmallTest
+    public void testOnPreDrawListenerRemoved_WhenNoLongerNeedToBlockDrawForIncognitoRestore() {
+        when(mIncognitoRestoreAppLaunchDrawBlockerMock.shouldBlockDraw()).thenReturn(true);
+        mInflationObserver.onPostInflationStartup();
+        verify(mViewTreeObserver, times(2))
+                .addOnPreDrawListener(mOnPreDrawListenerArgumentCaptor.capture());
+
+        // No longer need to block draw.
+        mAppLaunchDrawBlocker.setBlockDrawForIncognitoRestore(/*blockDraw=*/false);
+        mAppLaunchDrawBlocker.onActiveTabAvailable(true);
+
+        for (OnPreDrawListener listener : mOnPreDrawListenerArgumentCaptor.getAllValues()) {
+            assertTrue("Listener shouldn't be blocking the draw any longer.", listener.onPreDraw());
+            verify(mViewTreeObserver, times(1)).removeOnPreDrawListener(listener);
+        }
+
+        verify(mIncognitoRestoreAppLaunchDrawBlockerMock, times(1)).shouldBlockDraw();
+    }
+
     private void validateConstructorAndCaptureObservers() {
         verify(mActivityLifecycleDispatcher, times(2)).register(mLifecycleArgumentCaptor.capture());
         List<LifecycleObserver> observerList = mLifecycleArgumentCaptor.getAllValues();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
index cad7f944ae..d470118 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ui/IncognitoRestoreAppLaunchDrawBlockerUnitTest.java
@@ -5,13 +5,13 @@
 package org.chromium.chrome.browser.ui;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Intent;
 import android.os.Bundle;
@@ -23,6 +23,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
@@ -31,6 +33,7 @@
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.IntentHandler;
@@ -39,14 +42,17 @@
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.LifecycleObserver;
+import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 
 /**
  * Robolectric tests for {@link IncognitoRestoreAppLaunchDrawBlocker}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@LooperMode(Mode.PAUSED)
+@LooperMode(Mode.LEGACY)
 public class IncognitoRestoreAppLaunchDrawBlockerUnitTest {
     @Mock
     private Bundle mSavedInstanceStateMock;
@@ -60,7 +66,14 @@
     private TabModelSelector mTabModelSelectorMock;
     @Mock
     private ActivityLifecycleDispatcher mActivityLifecycleDispatcherMock;
+    @Mock
+    private Runnable mUnblockDrawRunnableMock;
+    @Captor
+    private ArgumentCaptor<LifecycleObserver> mLifecycleObserverArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverArgumentCaptor;
 
+    private OneshotSupplierImpl<Bundle> mSavedInstanceStateSupplier = new OneshotSupplierImpl<>();
     private ObservableSupplierImpl<TabModelSelector> mTabModelSelectorObservableSupplier =
             new ObservableSupplierImpl<>();
     private Supplier<Intent> mIntentSupplier = new Supplier<Intent>() {
@@ -70,27 +83,57 @@
             return mIntentMock;
         }
     };
+    private Supplier<Boolean> mShouldIgnoreIntentSupplier = new Supplier<Boolean>() {
+        @Nullable
+        @Override
+        public Boolean get() {
+            return mShouldIgnoreIntent;
+        }
+    };
 
+    private boolean mShouldIgnoreIntent;
     private IncognitoRestoreAppLaunchDrawBlocker mIncognitoRestoreAppLaunchDrawBlocker;
+    private NativeInitObserver mNativeInitObserver;
+    private TabModelSelectorObserver mTabModelSelectorObserver;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        IncognitoReauthManager.setIsIncognitoReauthFeatureAvailableForTesting(/*isAvailable=*/true);
+        IncognitoReauthManager.setIsIncognitoReauthFeatureAvailableForTesting(
+                /*isAvailable=*/false);
         CipherFactory.resetInstanceForTesting(mCipherFactoryMock);
         CommandLine.setInstanceForTesting(mCommandLineMock);
-
         mTabModelSelectorObservableSupplier.set(mTabModelSelectorMock);
-        doNothing().when(mActivityLifecycleDispatcherMock).register(any());
+        // This immediately tries to call the unblock draw runnable since the re-auth feature is
+        // set to false above and therefore the shouldBlockDraw returns false as well.
+        mSavedInstanceStateSupplier.set(mSavedInstanceStateMock);
         mIncognitoRestoreAppLaunchDrawBlocker = new IncognitoRestoreAppLaunchDrawBlocker(
-                mSavedInstanceStateMock, mTabModelSelectorObservableSupplier, mIntentSupplier,
-                mActivityLifecycleDispatcherMock);
+                mSavedInstanceStateSupplier, mTabModelSelectorObservableSupplier, mIntentSupplier,
+                mShouldIgnoreIntentSupplier, mActivityLifecycleDispatcherMock,
+                mUnblockDrawRunnableMock);
+        // This would get called once we set the |mSavedInstanceStateSupplier| because when the
+        // saved instance state is available we check again the
+        // mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw which returns false because
+        // the incognito re-auth feature is not available and we call the unblock runnable.
+        verify(mUnblockDrawRunnableMock, times(1)).run();
+
+        // Check that the we added the native init observer.
+        verify(mActivityLifecycleDispatcherMock, times(1))
+                .register(mLifecycleObserverArgumentCaptor.capture());
+        mNativeInitObserver = (NativeInitObserver) mLifecycleObserverArgumentCaptor.getValue();
+        assertNotNull("Didn't register the NativeInitObserver", mNativeInitObserver);
+
+        verify(mTabModelSelectorMock, times(1))
+                .addObserver(mTabModelSelectorObserverArgumentCaptor.capture());
+        mTabModelSelectorObserver = mTabModelSelectorObserverArgumentCaptor.getValue();
+        assertNotNull("Didn't add any observer.", mTabModelSelectorObserver);
     }
 
     @After
     public void tearDown() {
         verifyNoMoreInteractions(mSavedInstanceStateMock, mIntentMock, mCipherFactoryMock,
-                mCommandLineMock, mTabModelSelectorMock);
+                mCommandLineMock, mTabModelSelectorMock, mUnblockDrawRunnableMock,
+                mActivityLifecycleDispatcherMock);
     }
 
     @Test
@@ -189,6 +232,7 @@
     public void testShouldNotBlockDraw_WhenBothTabStateIsInitialized_And_NativeIsInitialized() {
         // Premise conditions
         IncognitoReauthManager.setIsIncognitoReauthFeatureAvailableForTesting(/*isAvailable=*/true);
+        mIncognitoRestoreAppLaunchDrawBlocker.resetIsUnblockDrawRunnableInvokedForTesting();
         doReturn(false).when(mCommandLineMock).hasSwitch(ChromeSwitches.NO_RESTORE_STATE);
         doReturn(true).when(mCipherFactoryMock).restoreFromBundle(mSavedInstanceStateMock);
         doReturn(true)
@@ -203,8 +247,7 @@
 
         // Test condition
         doReturn(true).when(mTabModelSelectorMock).isTabStateInitialized();
-        mIncognitoRestoreAppLaunchDrawBlocker.setIsNativeInitializationFinished(
-                /*initialized=*/true);
+        mNativeInitObserver.onFinishNativeInitialization();
         assertFalse(
                 "Should not block draw.", mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw());
 
@@ -217,7 +260,12 @@
                 .getBoolean(IncognitoRestoreAppLaunchDrawBlocker.IS_INCOGNITO_SELECTED, false);
         verify(mIntentMock, times(1))
                 .getBooleanExtra(IntentHandler.EXTRA_INVOKED_FROM_LAUNCH_NEW_INCOGNITO_TAB, false);
-        verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
+        // This is called again when we call mNativeInitObserver.onFinishNativeInitialization();
+        verify(mTabModelSelectorMock, times(2)).isTabStateInitialized();
+        // This is called when we call mNativeInitObserver.onFinishNativeInitialization() and since
+        // tab state is initialized as well, we will invoke the unblock runnable. Another time, it
+        // gets called is during our setUp method when the saved instance state becomes available.
+        verify(mUnblockDrawRunnableMock, times(2)).run();
     }
 
     @Test
@@ -239,8 +287,7 @@
 
         // Test condition
         doReturn(false).when(mTabModelSelectorMock).isTabStateInitialized();
-        mIncognitoRestoreAppLaunchDrawBlocker.setIsNativeInitializationFinished(
-                /*initialized=*/true);
+        mNativeInitObserver.onFinishNativeInitialization();
         assertTrue("Should block draw.", mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw());
 
         // Verify all the mocks were called.
@@ -252,7 +299,8 @@
                 .getBoolean(IncognitoRestoreAppLaunchDrawBlocker.IS_INCOGNITO_SELECTED, false);
         verify(mIntentMock, times(1))
                 .getBooleanExtra(IntentHandler.EXTRA_INVOKED_FROM_LAUNCH_NEW_INCOGNITO_TAB, false);
-        verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
+        // This is called again when we call mNativeInitObserver.onFinishNativeInitialization();
+        verify(mTabModelSelectorMock, times(2)).isTabStateInitialized();
     }
 
     @Test
@@ -274,9 +322,8 @@
 
         // Test condition
         doReturn(true).when(mTabModelSelectorMock).isTabStateInitialized();
-        mIncognitoRestoreAppLaunchDrawBlocker.setIsNativeInitializationFinished(
-                /*initialized=*/false);
-        assertTrue("Should block draw.", mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw());
+        assertTrue("Should block draw as native has not finished initialization.",
+                mIncognitoRestoreAppLaunchDrawBlocker.shouldBlockDraw());
 
         // Verify all the mocks were called.
         verify(mCommandLineMock, times(1)).hasSwitch(ChromeSwitches.NO_RESTORE_STATE);
@@ -289,4 +336,38 @@
                 .getBooleanExtra(IntentHandler.EXTRA_INVOKED_FROM_LAUNCH_NEW_INCOGNITO_TAB, false);
         verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
     }
+
+    @Test
+    @SmallTest
+    public void testUnblockDrawRunnableNotInvoked_WhenTabStateNotInitialized() {
+        doReturn(false).when(mTabModelSelectorMock).isTabStateInitialized();
+        mNativeInitObserver.onFinishNativeInitialization();
+
+        verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
+        verifyZeroInteractions(mUnblockDrawRunnableMock);
+    }
+
+    @Test
+    @SmallTest
+    public void testUnblockDrawRunnableNotInvoked_WhenNativeNotInitialized() {
+        doReturn(true).when(mTabModelSelectorMock).isTabStateInitialized();
+        mTabModelSelectorObserver.onTabStateInitialized();
+
+        verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
+        verifyZeroInteractions(mUnblockDrawRunnableMock);
+    }
+
+    @Test
+    @SmallTest
+    public void testUnblockDrawRunnableInvoked_WhenTabStateAndNativeIsInitialized() {
+        // We need to reset the boolean so that we can verify mUnblockDrawRunnable is invoked.
+        mIncognitoRestoreAppLaunchDrawBlocker.resetIsUnblockDrawRunnableInvokedForTesting();
+        doReturn(true).when(mTabModelSelectorMock).isTabStateInitialized();
+        mNativeInitObserver.onFinishNativeInitialization();
+
+        verify(mTabModelSelectorMock, times(1)).isTabStateInitialized();
+        // The previous one time gets called during the setup part when the saved instance state
+        // becomes available.
+        verify(mUnblockDrawRunnableMock, times(2)).run();
+    }
 }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 094cd53c..4b0554a 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6220,9 +6220,12 @@
   <message name="IDS_THEME_SELECTION_TITLE" desc="Title of the theme selection screen.">
     Personalize your display theme
   </message>
-  <message name="IDS_THEME_SELECTION_DESCRIPTION" desc="Description of the theme selection screen.">
+  <message name="IDS_THEME_SELECTION_DESCRIPTION_CLAMSHELL" desc="Description of the theme selection screen for clamshell devices.">
     Choose the theme that fits your needs. To change your theme, wallpaper, screen saver, and more, just right-click on the desktop.
   </message>
+  <message name="IDS_THEME_SELECTION_DESCRIPTION_TABLET" desc="Description of the theme selection screen for tablets.">
+    Choose the theme that fits your needs. To change your theme, wallpaper, screen saver, and more, just touch &amp; hold on the desktop.
+  </message>
   <message name="IDS_THEME_LIGHT_LABEL" desc="Name of the light theme button">
     Light theme
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION.png.sha1
deleted file mode 100644
index b94423cb5..0000000
--- a/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5c0727481711ec03f465f5bf9ab3ffe5bbcdd96d
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_CLAMSHELL.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_CLAMSHELL.png.sha1
new file mode 100644
index 0000000..3732a5a9
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_CLAMSHELL.png.sha1
@@ -0,0 +1 @@
+318bdf2c17160d7b51188e824c61812df1930c0c
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_TABLET.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_TABLET.png.sha1
new file mode 100644
index 0000000..3ba47559
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_THEME_SELECTION_DESCRIPTION_TABLET.png.sha1
@@ -0,0 +1 @@
+96e84d066138b135c7fc780d53368ce600209cd9
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 58428c7..3806a1b 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3136,6 +3136,9 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DIALOG_TITLE" desc="Title of the dialog that warns about deleting all site data.">
     Clear all data?
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE" desc="Title of the dialog that warns about deleting displayed site data.">
+    Clear displayed data?
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DESCRIPTION" desc="Label describing how much total disk space chrome is using, next to the clear all button">
     Total storage used by sites: <ph name="TOTAL_USAGE">$1<ex>8 GB</ex></ph>
   </message>
@@ -3151,12 +3154,21 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION" desc="Text for the dialog that warns about clearing storage used by all sites.">
     This will clear <ph name="TOTAL_USAGE">$1<ex>8 GB</ex></ph> of data stored by sites
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION" desc="Text for the dialog that warns about clearing storage used by displayed sites.">
+    This will clear <ph name="TOTAL_USAGE">$1<ex>8 GB</ex></ph> of data stored by displayed sites
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION_INSTALLED" desc="Text for the dialog that warns about clearing storage used by all sites, shown when there are installed apps.">
     This will clear <ph name="TOTAL_USAGE">$1<ex>8 GB</ex></ph> of data stored by sites and installed apps
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED" desc="Text for the dialog that warns about clearing storage used by displayed sites, shown when there are installed apps.">
+    This will clear <ph name="TOTAL_USAGE">$1<ex>8 GB</ex></ph> of data stored by displayed sites and installed apps
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_SIGN_OUT" desc="Text for the dialog that warns about being signed out when clearing storage used by all sites.">
     You'll be signed out of all sites, including in open tabs
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT" desc="Text for the dialog that warns about being signed out when clearing storage used by displayed sites.">
+    You'll be signed out of displayed sites, including in open tabs
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_DIALOG_TITLE" desc="Title of the dialog that warns about deleting all site data.">
     Clear site data
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION.png.sha1
new file mode 100644
index 0000000..acc7ca0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION.png.sha1
@@ -0,0 +1 @@
+9b6ccaa4c0ce3754141b74ebc9660d51870e9dee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED.png.sha1
new file mode 100644
index 0000000..4be27d4
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED.png.sha1
@@ -0,0 +1 @@
+943991ba5232db67547fa9ee0daea565942acd45
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..acc7ca0
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+9b6ccaa4c0ce3754141b74ebc9660d51870e9dee
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT.png.sha1
new file mode 100644
index 0000000..4be27d4
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT.png.sha1
@@ -0,0 +1 @@
+943991ba5232db67547fa9ee0daea565942acd45
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e87bc7f..6c3b712 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5586,6 +5586,8 @@
       "policy/messaging_layer/util/manual_test_heartbeat_event.h",
       "policy/messaging_layer/util/manual_test_heartbeat_event_factory.cc",
       "policy/messaging_layer/util/manual_test_heartbeat_event_factory.h",
+      "policy/restricted_mgs_policy_provider.cc",
+      "policy/restricted_mgs_policy_provider.h",
       "policy/system_features_disable_list_policy_handler.cc",
       "policy/system_features_disable_list_policy_handler.h",
       "renderer_context_menu/quick_answers_menu_observer.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 49832d2..b31dffa 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2523,7 +2523,6 @@
          kPasswordChangeVariationWithForcedDialogAfterEverySuccessfulSubmission),
      nullptr}};
 
-#if BUILDFLAG(IS_ANDROID)
 // The variations of --password-change-in-settings.
 const FeatureEntry::FeatureParam
     kPasswordChangeInSettingsVariationWithForcedWarningForEverySite[] = {
@@ -2539,6 +2538,7 @@
              kPasswordChangeInSettingsVariationWithForcedWarningForEverySite),
          nullptr}};
 
+#if BUILDFLAG(IS_ANDROID)
 // The variations of --touch-to-fill-password-submission.
 const FeatureEntry::FeatureParam
     kTouchToFillPasswordSubmissionWithConservativeHeuristics[] = {
@@ -6936,20 +6936,19 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(password_manager::features::kPasswordChange,
                                     kPasswordChangeFeatureVariations,
                                     "PasswordChangeFeatureVariations")},
-
-#if BUILDFLAG(IS_ANDROID)
     {"password-change-in-settings",
      flag_descriptions::kPasswordChangeInSettingsName,
-     flag_descriptions::kPasswordChangeInSettingsDescription, kOsAndroid,
+     flag_descriptions::kPasswordChangeInSettingsDescription, kOsAll,
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          password_manager::features::kPasswordChangeInSettings,
          kPasswordChangeInSettingsFeatureVariations,
          "PasswordChangeInSettingsFeatureVariations")},
+
+#if BUILDFLAG(IS_ANDROID)
     {"password-scripts-fetching",
      flag_descriptions::kPasswordScriptsFetchingName,
      flag_descriptions::kPasswordScriptsFetchingDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(password_manager::features::kPasswordScriptsFetching)},
-
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/TrustedWebActivityUmaRecorder.java b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/TrustedWebActivityUmaRecorder.java
index beb5292..6a49516 100644
--- a/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/TrustedWebActivityUmaRecorder.java
+++ b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/TrustedWebActivityUmaRecorder.java
@@ -13,6 +13,7 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.chrome.browser.browserservices.constants.LocationUpdateError;
 import org.chromium.chrome.browser.browserservices.constants.QualityEnforcementViolationType;
+import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.ukm.UkmRecorder;
 import org.chromium.content_public.browser.WebContents;
 
@@ -160,6 +161,16 @@
     }
 
     /**
+     * Records the notification permission request result for a TWA.
+     */
+    public static void recordNotificationPermissionRequestResult(
+            @ContentSettingValues int settingValue) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "TrustedWebActivity.Notification.PermissionRequestResult", settingValue,
+                ContentSettingValues.NUM_SETTINGS);
+    }
+
+    /**
      * Records whether or not a splash screen has been shown when launching a TWA.
      * Uses {@link TaskTraits#BEST_EFFORT} in order to not get in the way of loading the page.
      */
@@ -248,4 +259,9 @@
                 "TrustedWebActivity.QualityEnforcementViolation.Crashed", type,
                 QualityEnforcementViolationType.MAX_VALUE + 1);
     }
+
+    public void recordExtraCommandSuccess(String command, boolean success) {
+        RecordHistogram.recordBooleanHistogram(
+                "TrustedWebActivity.ExtraCommandSuccess." + command, success);
+    }
 }
diff --git a/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUmaRecorder.java b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUmaRecorder.java
index 2e9ebca..a1b3663 100644
--- a/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUmaRecorder.java
+++ b/chrome/browser/android/browserservices/metrics/java/src/org/chromium/chrome/browser/browserservices/metrics/WebApkUmaRecorder.java
@@ -159,6 +159,13 @@
                 settingValue, ContentSettingValues.NUM_SETTINGS);
     }
 
+    /** Records the notification permission request result for a WebAPK. */
+    public static void recordNotificationPermissionRequestResult(
+            @ContentSettingValues int settingValue) {
+        RecordHistogram.recordEnumeratedHistogram("WebApk.Notification.PermissionRequestResult",
+                settingValue, ContentSettingValues.NUM_SETTINGS);
+    }
+
     /**
      * Records whether installing a WebAPK from Google Play succeeded. If not, records the reason
      * that the install failed.
diff --git a/chrome/browser/apps/app_service/intent_util.cc b/chrome/browser/apps/app_service/intent_util.cc
index 01ae4da..e5b1abc9 100644
--- a/chrome/browser/apps/app_service/intent_util.cc
+++ b/chrome/browser/apps/app_service/intent_util.cc
@@ -637,6 +637,18 @@
   return apps::ConvertIntentFilterToMojomIntentFilter(CreateNoteTakingFilter());
 }
 
+apps::IntentFilterPtr CreateLockScreenFilter() {
+  auto intent_filter = std::make_unique<apps::IntentFilter>();
+  intent_filter->AddSingleValueCondition(apps::ConditionType::kAction,
+                                         kIntentActionStartOnLockScreen,
+                                         apps::PatternMatchType::kNone);
+  return intent_filter;
+}
+
+apps::mojom::IntentFilterPtr CreateLockScreenFilterMojom() {
+  return apps::ConvertIntentFilterToMojomIntentFilter(CreateLockScreenFilter());
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // TODO(crbug.com/1253219): Use FilePaths in intents to avoid dependency on
 // File Manager.
diff --git a/chrome/browser/apps/app_service/intent_util.h b/chrome/browser/apps/app_service/intent_util.h
index f2c1912..23ccec4 100644
--- a/chrome/browser/apps/app_service/intent_util.h
+++ b/chrome/browser/apps/app_service/intent_util.h
@@ -111,6 +111,14 @@
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
 apps::mojom::IntentFilterPtr CreateNoteTakingFilterMojom();
 
+// Create an intent filter for an app capable of running on the lock screen.
+apps::IntentFilterPtr CreateLockScreenFilter();
+
+// Create a mojom intent filter for an app capable of running on the lock
+// screen.
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+apps::mojom::IntentFilterPtr CreateLockScreenFilterMojom();
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Create an intent struct with filesystem:// type URLs from the file paths and
 // mime types of a list of files. This util has to live under chrome/ because it
diff --git a/chrome/browser/apps/app_service/intent_util_unittest.cc b/chrome/browser/apps/app_service/intent_util_unittest.cc
index 673cf37..d98f26a 100644
--- a/chrome/browser/apps/app_service/intent_util_unittest.cc
+++ b/chrome/browser/apps/app_service/intent_util_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/arc/intent_helper/intent_constants.h"
 #include "components/arc/intent_helper/intent_filter.h"
 #include "components/services/app_service/public/cpp/file_handler.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/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
@@ -425,9 +426,7 @@
   EXPECT_EQ(condition.condition_values[0]->value,
             apps_util::kIntentActionCreateNote);
 
-  apps::IntentPtr intent =
-      apps::ConvertMojomIntentToIntent(apps_util::CreateCreateNoteIntent());
-  EXPECT_TRUE(intent->MatchFilter(filter));
+  EXPECT_TRUE(apps_util::CreateCreateNoteIntent()->MatchFilter(filter));
 }
 
 // TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
@@ -443,7 +442,37 @@
             apps_util::kIntentActionCreateNote);
 
   EXPECT_TRUE(apps_util::IntentMatchesFilter(
-      apps_util::CreateCreateNoteIntent(), filter));
+      ConvertIntentToMojomIntent(apps_util::CreateCreateNoteIntent()), filter));
+}
+
+TEST_F(IntentUtilsTest, CreateLockScreenFilter) {
+  IntentFilterPtr filter = apps_util::CreateLockScreenFilter();
+
+  ASSERT_EQ(filter->conditions.size(), 1u);
+  const Condition& condition = *filter->conditions[0];
+  EXPECT_EQ(condition.condition_type, ConditionType::kAction);
+  ASSERT_EQ(condition.condition_values.size(), 1u);
+  EXPECT_EQ(condition.condition_values[0]->value,
+            apps_util::kIntentActionStartOnLockScreen);
+
+  EXPECT_TRUE(apps_util::CreateStartOnLockScreenIntent()->MatchFilter(filter));
+}
+
+// TODO(crbug.com/1253250): Remove after migrating to non-mojo AppService.
+TEST_F(IntentUtilsTest, CreateLockScreenFilterMojom) {
+  apps::mojom::IntentFilterPtr filter =
+      apps_util::CreateLockScreenFilterMojom();
+
+  ASSERT_EQ(filter->conditions.size(), 1u);
+  const apps::mojom::Condition& condition = *filter->conditions[0];
+  EXPECT_EQ(condition.condition_type, apps::mojom::ConditionType::kAction);
+  ASSERT_EQ(condition.condition_values.size(), 1u);
+  EXPECT_EQ(condition.condition_values[0]->value,
+            apps_util::kIntentActionStartOnLockScreen);
+
+  EXPECT_TRUE(apps_util::IntentMatchesFilter(
+      ConvertIntentToMojomIntent(apps_util::CreateStartOnLockScreenIntent()),
+      filter));
 }
 
 TEST_F(IntentUtilsTest, CreateIntentFiltersForChromeApp_FileHandlers) {
@@ -644,8 +673,7 @@
   EXPECT_EQ(condition.condition_values[0]->value,
             apps_util::kIntentActionCreateNote);
 
-  apps::IntentPtr intent =
-      apps::ConvertMojomIntentToIntent(apps_util::CreateCreateNoteIntent());
+  apps::IntentPtr intent = apps_util::CreateCreateNoteIntent();
   EXPECT_TRUE(intent->MatchFilter(filter));
 }
 
@@ -688,7 +716,8 @@
             apps_util::kIntentActionCreateNote);
 
   EXPECT_TRUE(apps_util::IntentMatchesFilter(
-      apps_util::CreateCreateNoteIntent(), filter));
+      apps::ConvertIntentToMojomIntent(apps_util::CreateCreateNoteIntent()),
+      filter));
 }
 
 TEST_F(IntentUtilsTest, CreateIntentFiltersForExtension_FileHandlers) {
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics.cc b/chrome/browser/apps/app_service/metrics/website_metrics.cc
index 41f61687..cf108993 100644
--- a/chrome/browser/apps/app_service/metrics/website_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/website_metrics.cc
@@ -5,11 +5,20 @@
 #include "chrome/browser/apps/app_service/metrics/website_metrics.h"
 
 #include "base/containers/contains.h"
+#include "chrome/browser/apps/app_service/web_contents_app_id_utils.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
+#include "components/history/core/browser/history_types.h"
+#include "components/webapps/browser/installable/installable_data.h"
+#include "components/webapps/browser/installable/installable_manager.h"
+#include "third_party/blink/public/common/manifest/manifest_util.h"
+#include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
+#include "third_party/blink/public/mojom/installation/installation.mojom.h"
+#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
 #include "ui/aura/window.h"
 
 namespace {
@@ -112,6 +121,8 @@
                                   selection);
       break;
     case TabStripModelChange::kReplaced:
+      OnTabStripModelChangeReplace(*change.GetReplace());
+      break;
     case TabStripModelChange::kMoved:
     case TabStripModelChange::kSelectionOnly:
       break;
@@ -131,7 +142,9 @@
 
 void WebsiteMetrics::OnURLsDeleted(history::HistoryService* history_service,
                                    const history::DeletionInfo& deletion_info) {
-  // TODO(crbug.com/1334173): Remove local records for urls in `deletion_info`.
+  // To simplify the implementation, remove all recorded urls no matter whatever
+  // `deletion_info`.
+  webcontents_to_ukm_key_.clear();
 }
 
 void WebsiteMetrics::HistoryServiceBeingDeleted(
@@ -165,6 +178,10 @@
     TabStripModel* tab_strip_model,
     const TabStripModelChange::Remove& remove,
     const TabStripSelectionChange& selection) {
+  for (const auto& removed_tab : remove.contents) {
+    webcontents_to_ukm_key_.erase(removed_tab.contents);
+  }
+
   // Last tab detached.
   if (tab_strip_model->count() == 0) {
     // Unobserve the activation client of the root window of the browser's aura
@@ -180,11 +197,17 @@
     auto it = window_to_web_contents_.find(window);
     if (it != window_to_web_contents_.end()) {
       webcontents_to_observer_map_.erase(it->second);
+      webcontents_to_ukm_key_.erase(it->second);
       window_to_web_contents_.erase(it);
     }
   }
 }
 
+void WebsiteMetrics::OnTabStripModelChangeReplace(
+    const TabStripModelChange::Replace& replace) {
+  webcontents_to_ukm_key_.erase(replace.old_contents);
+}
+
 void WebsiteMetrics::OnActiveTabChanged(aura::Window* window,
                                         content::WebContents* old_contents,
                                         content::WebContents* new_contents) {
@@ -210,8 +233,55 @@
   // url.
 }
 
-void WebsiteMetrics::OnWebContentsUpdated(content::WebContents* contents) {
-  // TODO(crbug.com/1334173): Update for the activated url.
+void WebsiteMetrics::OnWebContentsUpdated(content::WebContents* web_contents) {
+  // TODO(crbug.com/1334173): Calculate the usage time for the url.
+
+  // If there is an app for the url, we don't need to record the url, because
+  // the app metrics can record the usage time metrics.
+  if (GetInstanceAppIdForWebContents(web_contents).has_value()) {
+    webcontents_to_ukm_key_.erase(web_contents);
+    return;
+  }
+
+  // When the primary page of `web_contents` is changed called by
+  // contents::WebContentsObserver::PrimaryPageChanged(), set the visible url as
+  // default value for the ukm key url.
+  webcontents_to_ukm_key_[web_contents] = web_contents->GetVisibleURL();
+
+  // WebContents in app windows are filtered out in OnBrowserAdded. installed
+  // web apps opened in tabs are filtered out too. So every WebContents here
+  // must be a website not installed. Check the manifest to get the scope or the
+  // start url if there is a manifest.
+  webapps::InstallableParams params;
+  params.valid_manifest = true;
+  webapps::InstallableManager* manager =
+      webapps::InstallableManager::FromWebContents(web_contents);
+  DCHECK(manager);
+  manager->GetData(
+      params,
+      base::BindOnce(&WebsiteMetrics::OnDidPerformInstallableWebAppCheck,
+                     weak_factory_.GetWeakPtr(), web_contents));
+}
+
+void WebsiteMetrics::OnDidPerformInstallableWebAppCheck(
+    content::WebContents* web_contents,
+    const webapps::InstallableData& data) {
+  auto it = webcontents_to_ukm_key_.find(web_contents);
+  if (it == webcontents_to_ukm_key_.end()) {
+    // If the `web_contents` has been removed or replaced, we don't need to set
+    // the url.
+    return;
+  }
+
+  if (blink::IsEmptyManifest(data.manifest)) {
+    return;
+  }
+
+  if (!data.manifest.scope.is_empty()) {
+    it->second = data.manifest.scope;
+  } else if (!data.manifest.start_url.is_empty()) {
+    it->second = data.manifest.start_url;
+  }
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics.h b/chrome/browser/apps/app_service/metrics/website_metrics.h
index 367474ac..9dbe3856 100644
--- a/chrome/browser/apps/app_service/metrics/website_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/website_metrics.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_APPS_APP_SERVICE_METRICS_WEBSITE_METRICS_H_
 
 #include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_multi_source_observation.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ui/browser_list_observer.h"
@@ -18,10 +19,15 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/wm/public/activation_change_observer.h"
 #include "ui/wm/public/activation_client.h"
+#include "url/gurl.h"
 
 class Browser;
 class Profile;
 
+namespace webapps {
+struct InstallableData;
+}
+
 namespace apps {
 
 class WebsiteMetricsBrowserTest;
@@ -90,6 +96,8 @@
                                    TabStripModel* tab_strip_model,
                                    const TabStripModelChange::Remove& remove,
                                    const TabStripSelectionChange& selection);
+  void OnTabStripModelChangeReplace(
+      const TabStripModelChange::Replace& replace);
   void OnActiveTabChanged(aura::Window* window,
                           content::WebContents* old_contents,
                           content::WebContents* new_contents);
@@ -97,6 +105,11 @@
   // Called by |WebsiteMetrics::ActiveTabWebContentsObserver|.
   void OnWebContentsUpdated(content::WebContents* contents);
 
+  // Callback invoked by the InstallableManager once it has finished checking
+  // all other installable properties.
+  void OnDidPerformInstallableWebAppCheck(content::WebContents* web_contents,
+                                          const webapps::InstallableData& data);
+
   BrowserTabStripTracker browser_tab_strip_tracker_;
 
   // The map from the window to the active tab contents.
@@ -106,6 +119,17 @@
                  std::unique_ptr<ActiveTabWebContentsObserver>>
       webcontents_to_observer_map_;
 
+  // The map from the web_contents to the ukm key url. When the url for web
+  // contents is updated in OnWebContentsUpdated, we can get the previous url
+  // from this map to calculate the usage time for the previous url.
+  //
+  // If the url is used for an app, it won't be added to the map, because the
+  // app metrics can record the usage time metrics.
+  //
+  // If the website has a manifest, we might use the scope or the start url as
+  // the ukm key url. Otherwise, the visible url is used as the ukm key url.
+  std::map<content::WebContents*, GURL> webcontents_to_ukm_key_;
+
   // A set of observed activation clients for all browser's windows.
   base::ScopedMultiSourceObservation<wm::ActivationClient,
                                      wm::ActivationChangeObserver>
@@ -114,6 +138,8 @@
   base::ScopedObservation<history::HistoryService,
                           history::HistoryServiceObserver>
       history_observation_{this};
+
+  base::WeakPtrFactory<WebsiteMetrics> weak_factory_{this};
 };
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc b/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc
index 2eec710..7c2529b 100644
--- a/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc
+++ b/chrome/browser/apps/app_service/metrics/website_metrics_browsertest.cc
@@ -19,9 +19,14 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/history/core/browser/history_types.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+
+// TODO(crbug.com/1334173): Add tests to verify the scope and the start url are
+// used as the ukm key when a webpage has a manifest.
 
 namespace apps {
 
@@ -74,6 +79,10 @@
     return contents;
   }
 
+  void NavigateActiveTab(Browser* browser, const std::string& url) {
+    NavigateAndWait(browser, url, WindowOpenDisposition::CURRENT_TAB);
+  }
+
   content::WebContents* InsertForegroundTab(Browser* browser,
                                             const std::string& url) {
     return NavigateAndWait(browser, url,
@@ -110,6 +119,10 @@
     return website_metrics()->webcontents_to_observer_map_;
   }
 
+  std::map<content::WebContents*, GURL>& webcontents_to_ukm_key() {
+    return website_metrics()->webcontents_to_ukm_key_;
+  }
+
  protected:
   AppPlatformMetricsService* app_platform_metrics_service_ = nullptr;
 };
@@ -128,14 +141,17 @@
                              window_to_web_contents()[window]));
   EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
             GURL("https://a.example.org"));
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
 
   // Open a second tab in foreground with no app.
-  InsertForegroundTab(browser, "https://b.example.org");
+  auto* tab_app1 = InsertForegroundTab(browser, "https://b.example.org");
   EXPECT_EQ(1u, webcontents_to_observer_map().size());
   EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
                              window_to_web_contents()[window]));
   EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
             GURL("https://b.example.org"));
+  EXPECT_EQ(1u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app1], GURL("https://b.example.org"));
 
   // Open two more tabs in foreground and close them.
   auto* tab_app3 = InsertForegroundTab(browser, "https://c.example.org");
@@ -147,11 +163,17 @@
                              window_to_web_contents()[window]));
   EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
             GURL("https://d.example.org"));
+  EXPECT_EQ(3u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app3], GURL("https://c.example.org"));
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app4], GURL("https://d.example.org"));
 
   // Close in reverse order.
   int i = browser->tab_strip_model()->GetIndexOfWebContents(tab_app4);
   browser->tab_strip_model()->CloseWebContentsAt(
       i, TabStripModel::CLOSE_USER_GESTURE);
+  EXPECT_EQ(2u, webcontents_to_ukm_key().size());
+  EXPECT_FALSE(base::Contains(webcontents_to_ukm_key(), tab_app4));
+
   i = browser->tab_strip_model()->GetIndexOfWebContents(tab_app3);
   browser->tab_strip_model()->CloseWebContentsAt(
       i, TabStripModel::CLOSE_USER_GESTURE);
@@ -160,10 +182,46 @@
                              window_to_web_contents()[window]));
   EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
             GURL("https://b.example.org"));
+  EXPECT_EQ(1u, webcontents_to_ukm_key().size());
+  EXPECT_FALSE(base::Contains(webcontents_to_ukm_key(), tab_app3));
 
   browser->tab_strip_model()->CloseAllTabs();
-  EXPECT_EQ(0u, webcontents_to_observer_map().size());
-  EXPECT_EQ(0u, window_to_web_contents().size());
+  EXPECT_TRUE(webcontents_to_observer_map().empty());
+  EXPECT_TRUE(window_to_web_contents().empty());
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
+}
+
+IN_PROC_BROWSER_TEST_F(WebsiteMetricsBrowserTest, ForegroundTabNavigate) {
+  Browser* browser = CreateBrowser();
+  auto* window = browser->window()->GetNativeWindow();
+  EXPECT_EQ(1u, window_to_web_contents().size());
+
+  // Open a tab in foreground.
+  auto* tab_app = InsertForegroundTab(browser, "https://a.example.org");
+  EXPECT_EQ(1u, webcontents_to_observer_map().size());
+  EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
+                             window_to_web_contents()[window]));
+  EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
+            GURL("https://a.example.org"));
+  EXPECT_EQ(1u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app], GURL("https://a.example.org"));
+
+  // Navigate the foreground tab to a different url.
+  NavigateActiveTab(browser, "https://b.example.org");
+
+  EXPECT_EQ(1u, webcontents_to_observer_map().size());
+  EXPECT_EQ(1u, window_to_web_contents().size());
+  EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
+                             window_to_web_contents()[window]));
+  EXPECT_EQ(window_to_web_contents()[window]->GetVisibleURL(),
+            GURL("https://b.example.org"));
+  EXPECT_EQ(1u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app], GURL("https://b.example.org"));
+
+  browser->tab_strip_model()->CloseAllTabs();
+  EXPECT_TRUE(webcontents_to_observer_map().empty());
+  EXPECT_TRUE(window_to_web_contents().empty());
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
 }
 
 IN_PROC_BROWSER_TEST_F(WebsiteMetricsBrowserTest, MultipleBrowser) {
@@ -179,6 +237,9 @@
                              window_to_web_contents()[window1]));
   EXPECT_EQ(window_to_web_contents()[window1]->GetVisibleURL(),
             GURL("https://b.example.org"));
+  EXPECT_EQ(2u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app1], GURL("https://a.example.org"));
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app2], GURL("https://b.example.org"));
 
   auto* browser2 = CreateBrowser();
   auto* window2 = browser2->window()->GetNativeWindow();
@@ -191,6 +252,9 @@
                              window_to_web_contents()[window2]));
   EXPECT_EQ(window_to_web_contents()[window2]->GetVisibleURL(),
             GURL("https://d.example.org"));
+  EXPECT_EQ(4u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app3], GURL("https://c.example.org"));
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app4], GURL("https://d.example.org"));
 
   // Close tabs.
   int i = browser1->tab_strip_model()->GetIndexOfWebContents(tab_app1);
@@ -200,6 +264,8 @@
   EXPECT_EQ(2u, webcontents_to_observer_map().size());
   EXPECT_EQ(window_to_web_contents()[window1]->GetVisibleURL(),
             GURL("https://b.example.org"));
+  EXPECT_EQ(3u, webcontents_to_ukm_key().size());
+  EXPECT_FALSE(base::Contains(webcontents_to_ukm_key(), tab_app1));
 
   i = browser2->tab_strip_model()->GetIndexOfWebContents(tab_app3);
   browser2->tab_strip_model()->CloseWebContentsAt(
@@ -208,6 +274,8 @@
   EXPECT_EQ(2u, webcontents_to_observer_map().size());
   EXPECT_EQ(window_to_web_contents()[window2]->GetVisibleURL(),
             GURL("https://d.example.org"));
+  EXPECT_EQ(2u, webcontents_to_ukm_key().size());
+  EXPECT_FALSE(base::Contains(webcontents_to_ukm_key(), tab_app3));
 
   i = browser2->tab_strip_model()->GetIndexOfWebContents(tab_app4);
   browser2->tab_strip_model()->CloseWebContentsAt(
@@ -216,13 +284,70 @@
   EXPECT_EQ(1u, webcontents_to_observer_map().size());
   EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
                              window_to_web_contents()[window1]));
+  EXPECT_EQ(1u, webcontents_to_ukm_key().size());
+  EXPECT_FALSE(base::Contains(webcontents_to_ukm_key(), tab_app4));
 
   i = browser1->tab_strip_model()->GetIndexOfWebContents(tab_app2);
   browser1->tab_strip_model()->CloseWebContentsAt(
       i, TabStripModel::CLOSE_USER_GESTURE);
 
-  EXPECT_EQ(0u, window_to_web_contents().size());
-  EXPECT_EQ(0u, webcontents_to_observer_map().size());
+  EXPECT_TRUE(webcontents_to_observer_map().empty());
+  EXPECT_TRUE(window_to_web_contents().empty());
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
+}
+
+IN_PROC_BROWSER_TEST_F(WebsiteMetricsBrowserTest, OnURLsDeleted) {
+  // Setup: two browsers with one tabs each.
+  auto* browser1 = CreateBrowser();
+  auto* window1 = browser1->window()->GetNativeWindow();
+  auto* tab_app1 = InsertForegroundTab(browser1, "https://a.example.org");
+
+  auto* browser2 = CreateBrowser();
+  auto* window2 = browser2->window()->GetNativeWindow();
+  auto* tab_app2 = InsertForegroundTab(browser2, "https://b.example.org");
+
+  EXPECT_EQ(2u, window_to_web_contents().size());
+  EXPECT_EQ(2u, webcontents_to_observer_map().size());
+  EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
+                             window_to_web_contents()[window1]));
+  EXPECT_TRUE(base::Contains(webcontents_to_observer_map(),
+                             window_to_web_contents()[window2]));
+  EXPECT_EQ(window_to_web_contents()[window1]->GetVisibleURL(),
+            GURL("https://a.example.org"));
+  EXPECT_EQ(window_to_web_contents()[window2]->GetVisibleURL(),
+            GURL("https://b.example.org"));
+  EXPECT_EQ(2u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app1], GURL("https://a.example.org"));
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app2], GURL("https://b.example.org"));
+
+  // Simulate OnURLsDeleted is called.
+  website_metrics()->OnURLsDeleted(nullptr,
+                                   history::DeletionInfo::ForAllHistory());
+  EXPECT_EQ(2u, window_to_web_contents().size());
+  EXPECT_EQ(2u, webcontents_to_observer_map().size());
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
+
+  // Create 2 tabs for the 2 browsers separately.
+  auto* tab_app3 = InsertForegroundTab(browser1, "https://c.example.org");
+  auto* tab_app4 = InsertForegroundTab(browser2, "https://d.example.org");
+
+  EXPECT_EQ(2u, window_to_web_contents().size());
+  EXPECT_EQ(2u, webcontents_to_observer_map().size());
+  EXPECT_EQ(window_to_web_contents()[window1]->GetVisibleURL(),
+            GURL("https://c.example.org"));
+  EXPECT_EQ(window_to_web_contents()[window2]->GetVisibleURL(),
+            GURL("https://d.example.org"));
+  EXPECT_EQ(2u, webcontents_to_ukm_key().size());
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app3], GURL("https://c.example.org"));
+  EXPECT_EQ(webcontents_to_ukm_key()[tab_app4], GURL("https://d.example.org"));
+
+  // Close the browsers.
+  browser1->tab_strip_model()->CloseAllTabs();
+  browser2->tab_strip_model()->CloseAllTabs();
+
+  EXPECT_TRUE(window_to_web_contents().empty());
+  EXPECT_TRUE(webcontents_to_observer_map().empty());
+  EXPECT_TRUE(webcontents_to_ukm_key().empty());
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS b/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
index ae6e0ec..aef9c2c 100644
--- a/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
+++ b/chrome/browser/apps/platform_apps/api/enterprise_remote_apps/OWNERS
@@ -1,2 +1 @@
-jityao@google.com
 hendrich@chromium.org
diff --git a/chrome/browser/ash/app_mode/certificate_manager_dialog.cc b/chrome/browser/ash/app_mode/certificate_manager_dialog.cc
index 80319893..5539c2e 100644
--- a/chrome/browser/ash/app_mode/certificate_manager_dialog.cc
+++ b/chrome/browser/ash/app_mode/certificate_manager_dialog.cc
@@ -26,12 +26,9 @@
 
 namespace ash {
 
-CertificateManagerDialog::CertificateManagerDialog(
-    Profile* profile,
-    LoginWebDialog::Delegate* delegate,
-    gfx::NativeWindow window)
+CertificateManagerDialog::CertificateManagerDialog(Profile* profile,
+                                                   gfx::NativeWindow window)
     : LoginWebDialog(profile,
-                     delegate,
                      window,
                      std::u16string(),
                      GURL(chrome::kChromeUICertificateManagerDialogURL)) {}
diff --git a/chrome/browser/ash/app_mode/certificate_manager_dialog.h b/chrome/browser/ash/app_mode/certificate_manager_dialog.h
index 4435351..e44e64c 100644
--- a/chrome/browser/ash/app_mode/certificate_manager_dialog.h
+++ b/chrome/browser/ash/app_mode/certificate_manager_dialog.h
@@ -15,9 +15,7 @@
 // This dialog is used to manage user certificates from the kiosk launch screen.
 class CertificateManagerDialog : public LoginWebDialog {
  public:
-  CertificateManagerDialog(Profile* profile,
-                           LoginWebDialog::Delegate* delegate,
-                           gfx::NativeWindow window);
+  CertificateManagerDialog(Profile* profile, gfx::NativeWindow window);
   CertificateManagerDialog(const CertificateManagerDialog&) = delete;
   CertificateManagerDialog& operator=(const CertificateManagerDialog&) = delete;
   ~CertificateManagerDialog() override;
diff --git a/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
index 9ee49f3..2d8c280 100644
--- a/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -260,6 +260,7 @@
   }
   void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) override {}
+  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override {}
   void OnSurroundingTextChanged(const std::string& engine_id,
                                 const std::u16string& text,
                                 int cursor_pos,
diff --git a/chrome/browser/ash/arc/input_method_manager/input_connection_impl_unittest.cc b/chrome/browser/ash/arc/input_method_manager/input_connection_impl_unittest.cc
index e3803de..2f453a9 100644
--- a/chrome/browser/ash/arc/input_method_manager/input_connection_impl_unittest.cc
+++ b/chrome/browser/ash/arc/input_method_manager/input_connection_impl_unittest.cc
@@ -47,6 +47,7 @@
   void OnDeactivated(const std::string& engine_id) override {}
   void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) override {}
+  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override {}
   void OnSurroundingTextChanged(const std::string& engine_id,
                                 const std::u16string& text,
                                 int cursor_pos,
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 3ee731d..8267ee4 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -495,13 +495,20 @@
 }
 
 void DisplayOverlayController::OnMouseEvent(ui::MouseEvent* event) {
-  if (event->type() == ui::ET_MOUSE_PRESSED)
-    ProcessPressedEvent(*event);
+  if (display_mode_ == DisplayMode::kView ||
+      event->type() != ui::ET_MOUSE_PRESSED) {
+    return;
+  }
+
+  ProcessPressedEvent(*event);
 }
 
 void DisplayOverlayController::OnTouchEvent(ui::TouchEvent* event) {
-  if (event->type() == ui::ET_TOUCH_PRESSED)
-    ProcessPressedEvent(*event);
+  if (display_mode_ == DisplayMode::kView ||
+      event->type() != ui::ET_TOUCH_PRESSED) {
+    return;
+  }
+  ProcessPressedEvent(*event);
 }
 
 void DisplayOverlayController::OnColorModeChanged(bool dark_mode_enabled) {
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.cc b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
index 2b7a8439..d427a891 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.cc
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
@@ -490,7 +490,15 @@
     if (new_touch_event)
       return SendEventFinally(continuation, new_touch_event.get());
 
-    return DiscardEvent(continuation);
+    // TODO(b/237037540): workaround for b/233785660. Theoretically it
+    // should discard the event if original touch-move or touch-release with
+    // same ID is not rewritten due to missing original touch-press. But
+    // thinking of real world user cases, it's unlikely to trigger any issues
+    // with sending original event. The logic is already complicated in
+    // |RewriteEvent()| so here it uses a workaround. The menu entry will be
+    // removed and simplify the logic in future version, then it will be
+    // fundamentally improved.
+    return SendEvent(continuation, &event);
   }
 
   if (mouse_lock_ && mouse_lock_->Process(event))
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.cc b/chrome/browser/ash/arc/session/arc_session_manager.cc
index 2d2dc30..1679615e 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager.cc
@@ -19,15 +19,13 @@
 #include "ash/components/arc/session/arc_management_transition.h"
 #include "ash/components/arc/session/arc_session.h"
 #include "ash/components/arc/session/arc_session_runner.h"
+#include "ash/components/arc/session/serial_number_util.h"
 #include "ash/components/cryptohome/cryptohome_parameters.h"
 #include "ash/constants/ash_switches.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
-#include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/task/task_runner.h"
 #include "base/task/task_traits.h"
@@ -73,8 +71,6 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
-#include "crypto/random.h"
-#include "crypto/sha2.h"
 #include "ui/display/types/display_constants.h"
 
 // Enable VLOG level 1.
@@ -98,40 +94,12 @@
 absl::optional<bool> g_enable_check_android_management_in_tests;
 
 constexpr const char kArcSaltPath[] = "/var/lib/misc/arc_salt";
-constexpr const size_t kArcSaltFileSize = 16;
 
 constexpr const char kArcPrepareHostGeneratedDirJobName[] =
     "arc_2dprepare_2dhost_2dgenerated_2ddir";
 
 constexpr base::TimeDelta kWaitForPoliciesTimeout = base::Seconds(20);
 
-// Generates a unique, 20-character hex string from |chromeos_user| and
-// |salt| which can be used as Android's ro.boot.serialno and ro.serialno
-// properties. Note that Android treats serialno in a case-insensitive manner.
-// |salt| cannot be the hex-encoded one.
-// Note: The function must be the exact copy of the one in platform2/arc/setup/.
-std::string GenerateFakeSerialNumber(const std::string& chromeos_user,
-                                     const std::string& salt) {
-  constexpr size_t kMaxHardwareIdLen = 20;
-  const std::string hash(crypto::SHA256HashString(chromeos_user + salt));
-  return base::HexEncode(hash.data(), hash.length())
-      .substr(0, kMaxHardwareIdLen);
-}
-
-// Returns true if the hex-encoded salt in Local State is valid.
-bool IsValidHexSalt(const std::string& hex_salt) {
-  std::string salt;
-  if (!base::HexStringToString(hex_salt, &salt)) {
-    LOG(WARNING) << "Not a hex string: " << hex_salt;
-    return false;
-  }
-  if (salt.size() != kArcSaltFileSize) {
-    LOG(WARNING) << "Salt size invalid: " << salt.size();
-    return false;
-  }
-  return true;
-}
-
 // Maximum amount of time we'll wait for ARC to finish booting up. Once this
 // timeout expires, keep ARC running in case the user wants to file feedback,
 // but present the UI to try again.
@@ -241,67 +209,6 @@
   stability_metrics_manager->SetArcEnabledState(enabled);
 }
 
-// Generates and returns a serial number from the salt in |local_state| and
-// |chromeos_user|. When |local_state| does not have it (or has a corrupted
-// one), this function creates a new random salt. When creates it, the function
-// copies |arc_salt_on_disk| to |local_state| if |arc_salt_on_disk| is not
-// empty.
-std::string GetOrCreateSerialNumber(PrefService* local_state,
-                                    const std::string& chromeos_user,
-                                    const std::string& arc_salt_on_disk) {
-  DCHECK(local_state);
-  DCHECK(!chromeos_user.empty());
-
-  std::string hex_salt = local_state->GetString(prefs::kArcSerialNumberSalt);
-  if (hex_salt.empty() || !IsValidHexSalt(hex_salt)) {
-    // This path is taken 1) on the very first ARC boot, 2) on the first boot
-    // after powerwash, 3) on the first boot after upgrading to ARCVM, or 4)
-    // when the salt in local state is corrupted.
-    if (arc_salt_on_disk.empty()) {
-      // The device doesn't have the salt file for ARC container. Create it from
-      // scratch in the same way as ARC container.
-      char rand_value[kArcSaltFileSize];
-      crypto::RandBytes(rand_value, kArcSaltFileSize);
-      hex_salt = base::HexEncode(rand_value, kArcSaltFileSize);
-    } else {
-      // The device has the one for container. Reuse it for ARCVM.
-      DCHECK_EQ(kArcSaltFileSize, arc_salt_on_disk.size());
-      hex_salt =
-          base::HexEncode(arc_salt_on_disk.data(), arc_salt_on_disk.size());
-    }
-    local_state->SetString(prefs::kArcSerialNumberSalt, hex_salt);
-  }
-
-  // We store hex-encoded version of the salt in the local state, but to compute
-  // the serial number, we use the decoded version to be compatible with the
-  // arc-setup code for P.
-  std::string decoded_salt;
-  const bool result = base::HexStringToString(hex_salt, &decoded_salt);
-  DCHECK(result) << hex_salt;
-  return GenerateFakeSerialNumber(chromeos_user, decoded_salt);
-}
-
-// Reads a salt from |salt_path| and stores it in |out_salt|. Returns true
-// when the file read is successful or the file does not exist.
-bool ReadSaltOnDisk(const base::FilePath& salt_path, std::string* out_salt) {
-  DCHECK(out_salt);
-  if (!base::PathExists(salt_path)) {
-    VLOG(2) << "ARC salt file doesn't exist: " << salt_path;
-    return true;
-  }
-  if (!base::ReadFileToString(salt_path, out_salt)) {
-    PLOG(ERROR) << "Failed to read " << salt_path;
-    return false;
-  }
-  if (out_salt->size() != kArcSaltFileSize) {
-    LOG(WARNING) << "Ignoring invalid ARC salt on disk. size="
-                 << out_salt->size();
-    out_salt->clear();
-  }
-  VLOG(1) << "Successfully read ARC salt on disk: " << salt_path;
-  return true;
-}
-
 int GetSignInErrorCode(const arc::mojom::ArcSignInError* sign_in_error) {
   if (!sign_in_error)
     return 0;
@@ -465,10 +372,11 @@
   DCHECK(arc::IsArcVmEnabled());
 
   // For ARCVM, read |kArcSaltPath| if that exists.
-  std::string salt;
-  if (!ReadSaltOnDisk(base::FilePath(kArcSaltPath), &salt))
+  absl::optional<std::string> salt =
+      ReadSaltOnDisk(base::FilePath(kArcSaltPath));
+  if (!salt)
     return ArcSessionManager::ExpansionResult{{}, false};
-  return ArcSessionManager::ExpansionResult{salt, true};
+  return ArcSessionManager::ExpansionResult{std::move(*salt), true};
 }
 
 // Checks whether ARC DLCs needs to be installed/uninstalled. Currently,
@@ -611,28 +519,6 @@
   g_enable_check_android_management_in_tests = enable;
 }
 
-// static
-std::string ArcSessionManager::GenerateFakeSerialNumberForTesting(
-    const std::string& chromeos_user,
-    const std::string& salt) {
-  return GenerateFakeSerialNumber(chromeos_user, salt);
-}
-
-// static
-std::string ArcSessionManager::GetOrCreateSerialNumberForTesting(
-    PrefService* local_state,
-    const std::string& chromeos_user,
-    const std::string& arc_salt_on_disk) {
-  return GetOrCreateSerialNumber(local_state, chromeos_user, arc_salt_on_disk);
-}
-
-// static
-bool ArcSessionManager::ReadSaltOnDiskForTesting(
-    const base::FilePath& salt_path,
-    std::string* out_salt) {
-  return ReadSaltOnDisk(salt_path, out_salt);
-}
-
 void ArcSessionManager::OnSessionStopped(ArcStopReason reason,
                                          bool restarting) {
   if (restarting) {
diff --git a/chrome/browser/ash/arc/session/arc_session_manager.h b/chrome/browser/ash/arc/session/arc_session_manager.h
index 644716a..75001d8 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager.h
+++ b/chrome/browser/ash/arc/session/arc_session_manager.h
@@ -28,7 +28,6 @@
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 class ArcAppLauncher;
-class PrefService;
 class Profile;
 
 namespace arc {
@@ -138,15 +137,6 @@
   static void SetUiEnabledForTesting(bool enabled);
   static void SetArcTermsOfServiceOobeNegotiatorEnabledForTesting(bool enabled);
   static void EnableCheckAndroidManagementForTesting(bool enable);
-  static std::string GenerateFakeSerialNumberForTesting(
-      const std::string& chromeos_user,
-      const std::string& salt);
-  static std::string GetOrCreateSerialNumberForTesting(
-      PrefService* local_state,
-      const std::string& chromeos_user,
-      const std::string& arc_salt_on_disk);
-  static bool ReadSaltOnDiskForTesting(const base::FilePath& salt_path,
-                                       std::string* out_salt);
 
   // Returns true if ARC is allowed to run for the current session.
   // TODO(hidehiko): The name is very close to IsArcAllowedForProfile(), but
diff --git a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
index fcfee7b..350fb42 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
@@ -22,7 +22,6 @@
 #include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/notreached.h"
 #include "base/observer_list.h"
@@ -326,8 +325,6 @@
     return arc_session_manager_.get();
   }
 
-  PrefService* test_local_state() { return test_local_state_.get(); }
-
   bool WaitForDataRemoved(ArcSessionManager::State expected_state) {
     if (arc_session_manager()->state() !=
         ArcSessionManager::State::REMOVING_DATA_DIR)
@@ -340,25 +337,6 @@
     return true;
   }
 
-  static std::string GenerateFakeSerialNumber(const std::string& chromeos_user,
-                                              const std::string& salt) {
-    return ArcSessionManager::GenerateFakeSerialNumberForTesting(chromeos_user,
-                                                                 salt);
-  }
-
-  static std::string GetOrCreateSerialNumber(
-      PrefService* local_state,
-      const std::string& chromeos_user,
-      const std::string& arc_salt_on_disk) {
-    return ArcSessionManager::GetOrCreateSerialNumberForTesting(
-        local_state, chromeos_user, arc_salt_on_disk);
-  }
-
-  static bool ReadSaltOnDisk(const base::FilePath& salt_path,
-                             std::string* out_salt) {
-    return ArcSessionManager::ReadSaltOnDiskForTesting(salt_path, out_salt);
-  }
-
  private:
   void StartPreferenceSyncing() const {
     PrefServiceSyncableFromProfile(profile_.get())
@@ -1931,253 +1909,6 @@
   EXPECT_FALSE(observer.property_files_expansion_result().value());
 }
 
-TEST_F(ArcSessionManagerTest, GenerateFakeSerialNumber) {
-  // Check that the function always returns 20-character string.
-  EXPECT_EQ(20U,
-            GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc")
-                .size());
-  EXPECT_EQ(20U, GenerateFakeSerialNumber("", "").size());
-  EXPECT_EQ(20U, GenerateFakeSerialNumber("a", "b").size());
-
-  // Check that the function always returns the same ID for the same
-  // account and hwid_raw.
-  const std::string id_1 =
-      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
-  const std::string id_2 =
-      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
-  EXPECT_EQ(id_1, id_2);
-
-  // Generate an ID for a different account but for the same machine.
-  // Check that the ID is not the same as |id_1|.
-  const std::string id_3 =
-      GenerateFakeSerialNumber("mytestaccount2@gmail.com", "001122aabbcc");
-  EXPECT_NE(id_1, id_3);
-
-  // Generate an ID for a different machine but for the same account.
-  // Check that the ID is not the same as |id_1|.
-  const std::string id_4 =
-      GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aaddcc");
-  EXPECT_NE(id_1, id_4);
-
-  // Check that the function treats '\0' in |salt| properly.
-  using std::literals::string_literals::operator""s;
-  const std::string id_5 =
-      GenerateFakeSerialNumber("mytestaccount@gmail.com", "a\0b"s);
-  const std::string id_6 =
-      GenerateFakeSerialNumber("mytestaccount@gmail.com", "a\0c"s);
-  EXPECT_NE(id_5, id_6);
-}
-
-TEST_F(ArcSessionManagerTest, GetOrCreateSerialNumber) {
-  constexpr size_t kSerialNumberLen = 20;
-  constexpr size_t kHexSaltLen = 32;
-
-  const std::string chromeos_user = "user@gmail.com";
-  const std::string chromeos_user2 = "user2@gmail.com";
-  ASSERT_TRUE(
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
-
-  // Check that when neither the pref nor the salt file exists, a random salt
-  // is stored in the local state, and a serial number based on the salt is
-  // returned.
-  const std::string serialno_1 =
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
-  EXPECT_FALSE(serialno_1.empty());
-  EXPECT_EQ(kSerialNumberLen, serialno_1.size());
-
-  const std::string salt_1 =
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt_1.empty());
-  EXPECT_EQ(kHexSaltLen, salt_1.size());
-
-  // Calling the function again returns the same serial/salt.
-  EXPECT_EQ(serialno_1, GetOrCreateSerialNumber(test_local_state(),
-                                                chromeos_user, std::string()));
-  EXPECT_EQ(salt_1, test_local_state()->GetString(prefs::kArcSerialNumberSalt));
-
-  // A different user gets a different serial number, but the salt stays the
-  // same.
-  const std::string serialno_2 = GetOrCreateSerialNumber(
-      test_local_state(), chromeos_user2, std::string());
-  EXPECT_FALSE(serialno_2.empty());
-  EXPECT_EQ(kSerialNumberLen, serialno_2.size());
-  EXPECT_NE(serialno_1, serialno_2);
-  EXPECT_EQ(salt_1, test_local_state()->GetString(prefs::kArcSerialNumberSalt));
-
-  // Delete the salt in local state (which is what Chrome OS PowerWash does.)
-  test_local_state()->ClearPref(prefs::kArcSerialNumberSalt);
-  ASSERT_TRUE(
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
-
-  // Generate the salt and serial for |chromeos_user| again. Verify both are
-  // different than the previous ones.
-  const std::string serialno_3 =
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
-  EXPECT_FALSE(serialno_3.empty());
-  EXPECT_EQ(kSerialNumberLen, serialno_3.size());
-  EXPECT_NE(serialno_1, serialno_3);
-
-  const std::string salt_2 =
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt_2.empty());
-  EXPECT_EQ(kHexSaltLen, salt_2.size());
-  EXPECT_NE(salt_1, salt_2);
-
-  // Delete the salt in local state again.
-  test_local_state()->ClearPref(prefs::kArcSerialNumberSalt);
-  ASSERT_TRUE(
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt).empty());
-
-  // Pass |salt_on_disk| and verify hex-encoded version of the salt is stored
-  // in local state.
-  using std::literals::string_literals::operator""s;
-  const std::string salt_on_disk = "BAADDECAFC0\0FFEE"s;
-  const std::string salt_on_disk_hex =
-      base::HexEncode(salt_on_disk.data(), salt_on_disk.size());
-  const std::string serialno_4 =
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, salt_on_disk);
-  EXPECT_FALSE(serialno_4.empty());
-  EXPECT_EQ(kSerialNumberLen, serialno_4.size());
-  EXPECT_NE(serialno_1, serialno_4);
-
-  const std::string salt_3 =
-      test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_EQ(salt_on_disk_hex, salt_3);
-
-  // A different user gets a different serial number, but the salt stays the
-  // same. This time, pass a different salt on disk to verify it's ignored
-  // when a salt already exists in local state.
-  const std::string serialno_5 = GetOrCreateSerialNumber(
-      test_local_state(), chromeos_user2,
-      // Reverse |salt_on_disk| and pass it.
-      std::string(salt_on_disk.rbegin(), salt_on_disk.rend()));
-  EXPECT_FALSE(serialno_5.empty());
-  EXPECT_EQ(kSerialNumberLen, serialno_5.size());
-  EXPECT_NE(serialno_4, serialno_5);
-  // Local state still has the non-reversed one.
-  EXPECT_EQ(salt_on_disk_hex,
-            test_local_state()->GetString(prefs::kArcSerialNumberSalt));
-}
-
-// That shouldn't happen, but verify that the function can recover the state
-// even if local state has an invalid hex salt.
-TEST_F(ArcSessionManagerTest, GetOrCreateSerialNumber_InvalidLocalState) {
-  constexpr size_t kSaltLen = 16;
-  const std::string chromeos_user = "user@gmail.com";
-
-  // Manually set an invalid hex salt in local state, then call
-  // GetOrCreateSerialNumber. Verify the local state is overwritten by a valid
-  // one.
-  const std::string invalid_hex_salt_1 = "THIS_IS_NOT_A_HEX_STRING";
-  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
-                                invalid_hex_salt_1);
-  EXPECT_FALSE(
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
-          .empty());
-  std::string salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt.empty());
-  EXPECT_NE(invalid_hex_salt_1, salt);
-
-  // Do the same with a too short hex salt.
-  const std::string buf(kSaltLen + 1, 'x');
-  const std::string invalid_hex_salt_2 =
-      base::HexEncode(buf.data(), kSaltLen - 1);  // too short
-  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
-                                invalid_hex_salt_2);
-  EXPECT_FALSE(
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
-          .empty());
-  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt.empty());
-  EXPECT_NE(invalid_hex_salt_2, salt);
-
-  // Do the same with a too long one.
-  const std::string invalid_hex_salt_3 =
-      base::HexEncode(buf.data(), kSaltLen + 1);  // too long
-  test_local_state()->SetString(prefs::kArcSerialNumberSalt,
-                                invalid_hex_salt_3);
-  EXPECT_FALSE(
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
-          .empty());
-  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt.empty());
-  EXPECT_NE(invalid_hex_salt_3, salt);
-
-  // Test the valid case too.
-  const std::string valid_hex_salt = base::HexEncode(buf.data(), kSaltLen);
-  test_local_state()->SetString(prefs::kArcSerialNumberSalt, valid_hex_salt);
-  EXPECT_FALSE(
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string())
-          .empty());
-  salt = test_local_state()->GetString(prefs::kArcSerialNumberSalt);
-  EXPECT_FALSE(salt.empty());
-  EXPECT_EQ(valid_hex_salt, salt);
-}
-
-// Verify that GetOrCreateSerialNumber uses decoded salt when computing the
-// serial number.
-TEST_F(ArcSessionManagerTest, GetOrCreateSerialNumber_SerialNumberComputation) {
-  constexpr size_t kSaltLen = 16;
-  const std::string chromeos_user = "user@gmail.com";
-
-  // Set the |hex_salt| in local state.
-  const std::string hex_salt =
-      base::HexEncode(std::string(kSaltLen, 'x').data(), kSaltLen);
-  test_local_state()->SetString(prefs::kArcSerialNumberSalt, hex_salt);
-
-  // Get a serial number based on the hex salt.
-  const std::string serial_number =
-      GetOrCreateSerialNumber(test_local_state(), chromeos_user, std::string());
-  EXPECT_FALSE(serial_number.empty());
-
-  // Directly compute the serial number with the *hex* salt (which
-  // GetOrCreateSerialNumber is NOT supposed to do). Verify the returned
-  // serial number is NOT the same as the one from GetOrCreateSerialNumber.
-  EXPECT_NE(GenerateFakeSerialNumber(chromeos_user, hex_salt), serial_number);
-}
-
-// Tests that ReadSaltOnDisk can read a non-ASCII salt.
-TEST_F(ArcSessionManagerTest, ReadSaltOnDisk) {
-  constexpr int kSaltLen = 16;
-
-  std::string salt;
-  // Verify the function returns true when the file doesn't exist.
-  EXPECT_TRUE(ReadSaltOnDisk(base::FilePath("/nonexistent/path"), &salt));
-
-  // Create a valid arc_salt file.
-  using std::literals::string_literals::operator""s;
-  const std::string expected_salt_value = "BAADDECAFC0\0FFEE"s;
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath arc_salt_path = temp_dir.GetPath().Append("arc_salt");
-  ASSERT_EQ(kSaltLen, base::WriteFile(arc_salt_path, expected_salt_value.data(),
-                                      expected_salt_value.size()));
-
-  // Verify the function can read the salt file even when the file contains
-  // non-ASCII characters like '\0'.
-  EXPECT_TRUE(ReadSaltOnDisk(arc_salt_path, &salt));
-  EXPECT_EQ(expected_salt_value, salt);
-
-  // Change the mode to drop the r bit. Verify the function returns false
-  // when the file exists, but not readable.
-  ASSERT_TRUE(base::SetPosixFilePermissions(arc_salt_path, 0300));
-  EXPECT_FALSE(ReadSaltOnDisk(arc_salt_path, &salt));
-
-  // Create a different salt file that has corrupted data. Verify the function
-  // returns true but an empty |salt|.
-  arc_salt_path = temp_dir.GetPath().Append("arc_salt2");
-  ASSERT_TRUE(base::WriteFile(arc_salt_path,
-                              std::string(kSaltLen - 1, 'x')));  // too short
-  EXPECT_TRUE(ReadSaltOnDisk(arc_salt_path, &salt));
-  EXPECT_TRUE(salt.empty());
-
-  arc_salt_path = temp_dir.GetPath().Append("arc_salt3");
-  ASSERT_TRUE(base::WriteFile(arc_salt_path,
-                              std::string(kSaltLen + 1, 'x')));  // too long
-  EXPECT_TRUE(ReadSaltOnDisk(arc_salt_path, &salt));
-  EXPECT_TRUE(salt.empty());
-}
-
 // Tests that TrimVmMemory doesn't crash.
 TEST_F(ArcSessionManagerTest, TrimVmMemory) {
   bool callback_called = false;
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index be6ce7e3..bef4048 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -113,7 +113,9 @@
         LOG(WARNING) << "Vendor token provided, bypassing hardware checks.";
         return AllowStatus::kAllowed;
       }
-      return AllowStatus::kIncorrectToken;
+      return CpuRegexMatches("Ryzen [57]") && HasMemory(7 * kGibi)
+                 ? AllowStatus::kAllowed
+                 : AllowStatus::kHardwareChecksFailed;
     } else if (IsBoard("draco")) {
       return AllowStatus::kAllowed;
     }
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
index cdf57dd..1a240eef 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index 506130a..73394d8 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -547,6 +547,8 @@
 
   result->attestation_for_content_protection_enabled = MojoOptionalBool::kUnset;
   result->device_ephemeral_users_enabled = MojoOptionalBool::kUnset;
+  result->device_restricted_managed_guest_session_enabled =
+      MojoOptionalBool::kUnset;
   if (ash::CrosSettings::IsInitialized()) {
     // It's expected that the CrosSettings values are trusted. The only
     // theoretical exception is when device ownership is taken on consumer
@@ -596,6 +598,16 @@
                                                      ? MojoOptionalBool::kTrue
                                                      : MojoOptionalBool::kFalse;
       }
+
+      bool device_restricted_managed_guest_session_enabled = false;
+      if (cros_settings->GetBoolean(
+              ash::kDeviceRestrictedManagedGuestSessionEnabled,
+              &device_restricted_managed_guest_session_enabled)) {
+        result->device_restricted_managed_guest_session_enabled =
+            device_restricted_managed_guest_session_enabled
+                ? MojoOptionalBool::kTrue
+                : MojoOptionalBool::kFalse;
+      }
     } else {
       LOG(WARNING) << "Unexpected crossettings trusted values status: "
                    << trusted_result;
diff --git a/chrome/browser/ash/crosapi/crosapi_util_unittest.cc b/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
index 554e614..379751f 100644
--- a/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
@@ -123,6 +123,8 @@
             crosapi::mojom::DeviceSettings::OptionalBool::kUnset);
   EXPECT_EQ(settings->device_system_wide_tracing_enabled,
             crosapi::mojom::DeviceSettings::OptionalBool::kUnset);
+  EXPECT_EQ(settings->device_restricted_managed_guest_session_enabled,
+            crosapi::mojom::DeviceSettings::OptionalBool::kUnset);
 }
 
 TEST_F(CrosapiUtilTest, DeviceSettingsWithData) {
@@ -137,6 +139,9 @@
   testing_profile_.ScopedCrosSettingsTestHelper()
       ->GetStubbedProvider()
       ->SetBoolean(ash::kAccountsPrefEphemeralUsersEnabled, false);
+  testing_profile_.ScopedCrosSettingsTestHelper()
+      ->GetStubbedProvider()
+      ->SetBoolean(ash::kDeviceRestrictedManagedGuestSessionEnabled, true);
 
   base::Value allowlist(base::Value::Type::LIST);
   base::Value ids(base::Value::Type::DICTIONARY);
@@ -155,6 +160,8 @@
             crosapi::mojom::DeviceSettings::OptionalBool::kTrue);
   EXPECT_EQ(settings->device_ephemeral_users_enabled,
             crosapi::mojom::DeviceSettings::OptionalBool::kFalse);
+  EXPECT_EQ(settings->device_restricted_managed_guest_session_enabled,
+            crosapi::mojom::DeviceSettings::OptionalBool::kTrue);
   ASSERT_EQ(settings->usb_detachable_allow_list->usb_device_ids.size(), 1u);
   EXPECT_EQ(
       settings->usb_detachable_allow_list->usb_device_ids[0]->has_vendor_id,
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task.cc b/chrome/browser/ash/file_manager/empty_trash_io_task.cc
index d93d3d8..f114062 100644
--- a/chrome/browser/ash/file_manager/empty_trash_io_task.cc
+++ b/chrome/browser/ash/file_manager/empty_trash_io_task.cc
@@ -72,26 +72,28 @@
     return;
   }
 
-  RemoveTrashDirectory(it);
+  RemoveTrashSubDirectory(it, kFilesFolderName);
 }
 
-void EmptyTrashIOTask::RemoveTrashDirectory(
-    TrashPathsMap::const_iterator& trash_location) {
+void EmptyTrashIOTask::RemoveTrashSubDirectory(
+    TrashPathsMap::const_iterator& trash_location,
+    const std::string& folder_name_to_remove) {
   const base::FilePath trash_path =
       trash_location->second.trash_parent_path.Append(
           trash_location->second.relative_folder_path);
   const storage::FileSystemURL trash_url =
       file_system_context_->CreateCrackedFileSystemURL(
           storage_key_, storage::FileSystemType::kFileSystemTypeLocal,
-          trash_path);
+          trash_path.Append(folder_name_to_remove));
 
   progress_.outputs.emplace_back(trash_url, absl::nullopt);
 
   auto complete_callback = base::BindPostTask(
       base::SequencedTaskRunnerHandle::Get(),
-      base::BindOnce(&EmptyTrashIOTask::OnRemoveTrashDirectory,
+      base::BindOnce(&EmptyTrashIOTask::OnRemoveTrashSubDirectory,
                      weak_ptr_factory_.GetWeakPtr(),
-                     base::OwnedRef(trash_location)));
+                     base::OwnedRef(trash_location),
+                     std::move(folder_name_to_remove)));
 
   content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
       FROM_HERE,
@@ -101,21 +103,27 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void EmptyTrashIOTask::OnRemoveTrashDirectory(TrashPathsMap::const_iterator& it,
-                                              base::File::Error status) {
+void EmptyTrashIOTask::OnRemoveTrashSubDirectory(
+    TrashPathsMap::const_iterator& it,
+    const std::string& removed_folder_name,
+    base::File::Error status) {
   progress_.outputs[progress_.outputs.size() - 1].error = status;
   if (status != base::File::FILE_OK) {
     LOG(ERROR) << "Failed to remove trash directory " << status;
     Complete(State::kError);
     return;
   }
+  if (removed_folder_name == kFilesFolderName) {
+    RemoveTrashSubDirectory(it, kInfoFolderName);
+    return;
+  }
   it++;
   if (it == enabled_trash_locations_.end()) {
     Complete(State::kSuccess);
     return;
   }
 
-  RemoveTrashDirectory(it);
+  RemoveTrashSubDirectory(it, kFilesFolderName);
 }
 
 // Calls the completion callback for the task. `progress_` should not be
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task.h b/chrome/browser/ash/file_manager/empty_trash_io_task.h
index 1818410..0f6c93c 100644
--- a/chrome/browser/ash/file_manager/empty_trash_io_task.h
+++ b/chrome/browser/ash/file_manager/empty_trash_io_task.h
@@ -47,14 +47,16 @@
   void Cancel() override;
 
  private:
-  // Removes the entire trash directory (e.g. .Trash) recursively. It only
-  // iterates over the enabled trash locations.
-  void RemoveTrashDirectory(TrashPathsMap::const_iterator& trash_location);
+  // Removes the entire trash subdirectory (e.g. .Trash/files) recursively. It
+  // only iterates over the enabled trash locations.
+  void RemoveTrashSubDirectory(TrashPathsMap::const_iterator& trash_location,
+                               const std::string& folder_name_to_remove);
 
   // After removing the trash directory, continue iterating until there are no
   // more enabled trash directories left.
-  void OnRemoveTrashDirectory(TrashPathsMap::const_iterator& it,
-                              base::File::Error status);
+  void OnRemoveTrashSubDirectory(TrashPathsMap::const_iterator& it,
+                                 const std::string& removed_folder_name,
+                                 base::File::Error status);
 
   // Finish up and invoke the `complete_callback_`.
   void Complete(State state);
diff --git a/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc
index a06ab68..0d21f67 100644
--- a/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc
+++ b/chrome/browser/ash/file_manager/empty_trash_io_task_unittest.cc
@@ -27,7 +27,7 @@
 
 using ::base::test::RunClosure;
 using ::testing::AllOf;
-using ::testing::ElementsAre;
+using ::testing::ContainerEq;
 using ::testing::Field;
 using ::testing::IsEmpty;
 
@@ -43,22 +43,53 @@
   return testing::ExplainMatchResult(matcher, paths, result_listener);
 }
 
+struct TrashDirectoriesAndSubDirectories {
+  std::vector<base::FilePath> trash_directories;
+  std::vector<base::FilePath> trash_subdirectories;
+};
+
 class EmptyTrashIOTaskTest : public TrashBaseTest {
  public:
   EmptyTrashIOTaskTest() = default;
 
   EmptyTrashIOTaskTest(const EmptyTrashIOTaskTest&) = delete;
   EmptyTrashIOTaskTest& operator=(const EmptyTrashIOTaskTest&) = delete;
+
+  base::FilePath SetupTrashDirectory(
+      const base::FilePath& trash_parent_path,
+      const std::string& relative_trash_folder,
+      std::vector<base::FilePath>& trash_subdirectories) {
+    base::FilePath trash_directory =
+        trash_parent_path.Append(relative_trash_folder);
+    EXPECT_TRUE(EnsureTrashDirectorySetup(trash_directory));
+
+    trash_subdirectories.emplace_back(trash_directory.Append(kFilesFolderName));
+    trash_subdirectories.emplace_back(trash_directory.Append(kInfoFolderName));
+    return trash_directory;
+  }
+
+  const TrashDirectoriesAndSubDirectories SetupTrashAndReturnDirectories() {
+    TrashDirectoriesAndSubDirectories directories;
+
+    // Setup ~/MyFiles/.Trash
+    directories.trash_directories.emplace_back(SetupTrashDirectory(
+        my_files_dir_, kTrashFolderName, directories.trash_subdirectories));
+
+    // Setup ~/MyFiles/Downloads/.Trash
+    directories.trash_directories.emplace_back(SetupTrashDirectory(
+        downloads_dir_, kTrashFolderName, directories.trash_subdirectories));
+
+    // Setup /media/fuse/termina_hash_pengiun/.local/share/Trash
+    directories.trash_directories.emplace_back(SetupTrashDirectory(
+        crostini_dir_, ".local/share/Trash", directories.trash_subdirectories));
+
+    return directories;
+  }
 };
 
 TEST_F(EmptyTrashIOTaskTest, EnabledTrashDirsAreTrashed) {
-  base::FilePath my_files_trash_dir = my_files_dir_.Append(kTrashFolderName);
-  base::FilePath downloads_trash_dir = downloads_dir_.Append(kTrashFolderName);
-  base::FilePath crostini_trash_dir =
-      crostini_dir_.Append(".local/share/Trash");
-  ASSERT_TRUE(EnsureTrashDirectorySetup(my_files_trash_dir));
-  ASSERT_TRUE(EnsureTrashDirectorySetup(downloads_trash_dir));
-  ASSERT_TRUE(EnsureTrashDirectorySetup(crostini_trash_dir));
+  const auto [trash_directories, trash_subdirectories] =
+      SetupTrashAndReturnDirectories();
 
   base::RunLoop run_loop;
   base::MockRepeatingCallback<void(const ProgressStatus&)> progress_callback;
@@ -66,13 +97,12 @@
 
   // We should get one complete callback to be invoked once with a success
   // message and the list of outputs containing the enabled trash locations.
-  EXPECT_CALL(complete_callback,
-              Run(AllOf(Field(&ProgressStatus::state, State::kSuccess),
-                        Field(&ProgressStatus::sources, IsEmpty()),
-                        Field(&ProgressStatus::outputs,
-                              EntryStatusPaths(ElementsAre(
-                                  my_files_trash_dir, downloads_trash_dir,
-                                  crostini_trash_dir))))))
+  EXPECT_CALL(
+      complete_callback,
+      Run(AllOf(Field(&ProgressStatus::state, State::kSuccess),
+                Field(&ProgressStatus::sources, IsEmpty()),
+                Field(&ProgressStatus::outputs,
+                      EntryStatusPaths(ContainerEq(trash_subdirectories))))))
       .WillOnce(RunClosure(run_loop.QuitClosure()));
 
   EmptyTrashIOTask task(kTestStorageKey, profile_.get(), file_system_context_,
@@ -80,9 +110,16 @@
   task.Execute(progress_callback.Get(), complete_callback.Get());
   run_loop.Run();
 
-  ASSERT_FALSE(base::PathExists(my_files_trash_dir));
-  ASSERT_FALSE(base::PathExists(downloads_trash_dir));
-  ASSERT_FALSE(base::PathExists(crostini_trash_dir));
+  // Ensure the trash parent paths still exist, e.g. ~/MyFiles/.Trash
+  for (const auto& directory_path : trash_directories) {
+    ASSERT_TRUE(base::PathExists(directory_path));
+  }
+
+  // Ensure the subdirectories in the trash folder are removed, e.g.
+  // ~/MyFiles/.Trash/{files,info}
+  for (const auto& subdirectory_path : trash_subdirectories) {
+    ASSERT_FALSE(base::PathExists(subdirectory_path));
+  }
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 5bffb5a..312de65 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -661,15 +661,7 @@
              IDS_FILE_BROWSER_DROP_TARGET_OPENING_LINUX_FILES);
   SET_STRING("GO_TO_FILE_LOCATION_BUTTON_LABEL",
              IDS_FILE_BROWSER_GO_TO_FILE_LOCATION_BUTTON_LABEL);
-  SET_STRING("OPEN_WITH_VERB_BUTTON_LABEL",
-             IDS_FILE_BROWSER_OPEN_WITH_VERB_BUTTON_LABEL);
-  SET_STRING("ADD_TO_VERB_BUTTON_LABEL",
-             IDS_FILE_BROWSER_ADD_TO_VERB_BUTTON_LABEL);
-  SET_STRING("PACK_WITH_VERB_BUTTON_LABEL",
-             IDS_FILE_BROWSER_PACK_WITH_VERB_BUTTON_LABEL);
   SET_STRING("SEND_FEEDBACK", IDS_FILE_BROWSER_SEND_FEEDBACK_BUTTON_LABEL);
-  SET_STRING("SHARE_WITH_VERB_BUTTON_LABEL",
-             IDS_FILE_BROWSER_SHARE_WITH_VERB_BUTTON_LABEL);
   SET_STRING("PASTE_BUTTON_LABEL", IDS_FILE_BROWSER_PASTE_BUTTON_LABEL);
   SET_STRING("PASTE_INTO_FOLDER_BUTTON_LABEL",
              IDS_FILE_BROWSER_PASTE_INTO_FOLDER_BUTTON_LABEL);
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 55d9b0c..128f150 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -186,6 +186,11 @@
           volume.mount_path().BaseName().AsUTF8Unsafe());
 }
 
+std::string FuseBoxMTPSubdir(const std::string& storage_info_location) {
+  auto suffix = base::TrimString(storage_info_location, "/", base::TRIM_ALL);
+  return std::string(kMtpVolumeIdPrefix).append(std::string(suffix));
+}
+
 std::string GetMountPointNameForMediaStorage(
     const storage_monitor::StorageInfo& info) {
   std::string name(kFileManagerMTPMountNamePrefix);
@@ -1429,13 +1434,16 @@
   DCHECK(mtp_file_system_url.is_valid());
 
   // Attach the MTP storage device to the fusebox daemon.
+  std::string subdir = FuseBoxMTPSubdir(info.location());
   fusebox_mounter_->AttachStorage(
-      "mtp", url, read_only,
+      subdir, url, read_only,
       base::BindOnce(&VolumeManager::OnFuseboxAttachStorageMTP,
-                     weak_ptr_factory_.GetWeakPtr(), fsid, label, read_only));
+                     weak_ptr_factory_.GetWeakPtr(), subdir, fsid, label,
+                     read_only));
 }
 
-void VolumeManager::OnFuseboxAttachStorageMTP(const std::string& fsid,
+void VolumeManager::OnFuseboxAttachStorageMTP(const std::string& subdir,
+                                              const std::string& fsid,
                                               const std::string& label,
                                               bool read_only,
                                               int error) {
@@ -1444,7 +1452,7 @@
     return;
 
   // Create a Volume for the fusebox MTP storage device.
-  const base::FilePath mount_path = base::FilePath::FromUTF8Unsafe("mtp");
+  const base::FilePath mount_path = base::FilePath(subdir);
   std::unique_ptr<Volume> volume =
       Volume::CreateForFuseBoxMTP(mount_path, label, read_only);
 
@@ -1497,7 +1505,8 @@
     mount_points->RevokeFileSystem(util::kFuseBox + fsid);
 
     // Detach the fusebox MTP storage device from the fusebox daemon.
-    fusebox_mounter_->DetachStorage("mtp", base::DoNothing());
+    std::string subdir = FuseBoxMTPSubdir(info.location());
+    fusebox_mounter_->DetachStorage(subdir, base::DoNothing());
     return;
   }
 }
diff --git a/chrome/browser/ash/file_manager/volume_manager.h b/chrome/browser/ash/file_manager/volume_manager.h
index c50da29..ec386db 100644
--- a/chrome/browser/ash/file_manager/volume_manager.h
+++ b/chrome/browser/ash/file_manager/volume_manager.h
@@ -503,7 +503,8 @@
                       const std::string& display_name);
   void RemoveSmbFsVolume(const base::FilePath& mount_point);
 
-  void OnFuseboxAttachStorageMTP(const std::string& fsid,
+  void OnFuseboxAttachStorageMTP(const std::string& subdir,
+                                 const std::string& fsid,
                                  const std::string& label,
                                  bool read_only,
                                  int error);
diff --git a/chrome/browser/ash/input_method/input_method_engine.cc b/chrome/browser/ash/input_method/input_method_engine.cc
index 708f891a..ed9dbe3 100644
--- a/chrome/browser/ash/input_method/input_method_engine.cc
+++ b/chrome/browser/ash/input_method/input_method_engine.cc
@@ -681,6 +681,11 @@
   observer_->OnCompositionBoundsChanged(bounds);
 }
 
+void InputMethodEngine::SetCaretBounds(const gfx::Rect& caret_bounds)
+{
+  observer_->OnCaretBoundsChanged(caret_bounds);
+}
+
 void InputMethodEngine::PropertyActivate(const std::string& property_name) {
   observer_->OnMenuItemActivated(active_component_id_, property_name);
 }
diff --git a/chrome/browser/ash/input_method/input_method_engine.h b/chrome/browser/ash/input_method/input_method_engine.h
index 3ecf9a07..1c832b04 100644
--- a/chrome/browser/ash/input_method/input_method_engine.h
+++ b/chrome/browser/ash/input_method/input_method_engine.h
@@ -235,6 +235,7 @@
                           uint32_t anchor_pos,
                           uint32_t offset_pos) override;
   void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) override;
+  void SetCaretBounds(const gfx::Rect& caret_bounds) override;
   void PropertyActivate(const std::string& property_name) override;
   void CandidateClicked(uint32_t index) override;
   void AssistiveWindowButtonClicked(
diff --git a/chrome/browser/ash/input_method/input_method_engine_observer.h b/chrome/browser/ash/input_method/input_method_engine_observer.h
index 276a516..6723bda 100644
--- a/chrome/browser/ash/input_method/input_method_engine_observer.h
+++ b/chrome/browser/ash/input_method/input_method_engine_observer.h
@@ -72,6 +72,9 @@
   virtual void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) = 0;
 
+  // Called when the caret bounds change.
+  virtual void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) = 0;
+
   // Called when a surrounding text is changed.
   virtual void OnSurroundingTextChanged(const std::string& engine_id,
                                         const std::u16string& text,
diff --git a/chrome/browser/ash/input_method/mock_input_method_engine.cc b/chrome/browser/ash/input_method/mock_input_method_engine.cc
index a994022..798bfb6 100644
--- a/chrome/browser/ash/input_method/mock_input_method_engine.cc
+++ b/chrome/browser/ash/input_method/mock_input_method_engine.cc
@@ -42,6 +42,9 @@
 void MockInputMethodEngine::SetCompositionBounds(
     const std::vector<gfx::Rect>& bounds) {}
 
+void MockInputMethodEngine::SetCaretBounds(
+    const gfx::Rect& caret_bounds) {}
+
 ui::VirtualKeyboardController*
 MockInputMethodEngine::GetVirtualKeyboardController() const {
   return nullptr;
diff --git a/chrome/browser/ash/input_method/mock_input_method_engine.h b/chrome/browser/ash/input_method/mock_input_method_engine.h
index 6945666..a07bda1 100644
--- a/chrome/browser/ash/input_method/mock_input_method_engine.h
+++ b/chrome/browser/ash/input_method/mock_input_method_engine.h
@@ -42,6 +42,7 @@
                           uint32_t anchor_pos,
                           uint32_t offset_pos) override;
   void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) override;
+  void SetCaretBounds(const gfx::Rect& caret_bounds) override;
   ui::VirtualKeyboardController* GetVirtualKeyboardController() const override;
   void PropertyActivate(const std::string& property_name) override;
   void CandidateClicked(uint32_t index) override;
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
index d50bcf4..d4199e2 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -881,6 +881,11 @@
   ime_base_observer_->OnCompositionBoundsChanged(bounds);
 }
 
+void NativeInputMethodEngineObserver::OnCaretBoundsChanged(
+    const gfx::Rect& caret_bounds) {
+  ime_base_observer_->OnCaretBoundsChanged(caret_bounds);
+}
+
 void NativeInputMethodEngineObserver::OnSurroundingTextChanged(
     const std::string& engine_id,
     const std::u16string& text,
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.h b/chrome/browser/ash/input_method/native_input_method_engine_observer.h
index a040352..fc8b1ec3 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.h
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.h
@@ -70,6 +70,7 @@
   void OnDeactivated(const std::string& engine_id) override;
   void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) override;
+  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override;
   void OnSurroundingTextChanged(const std::string& engine_id,
                                 const std::u16string& text,
                                 int cursor_pos,
diff --git a/chrome/browser/ash/input_method/stub_input_method_engine_observer.h b/chrome/browser/ash/input_method/stub_input_method_engine_observer.h
index d07b699..95cc76e 100644
--- a/chrome/browser/ash/input_method/stub_input_method_engine_observer.h
+++ b/chrome/browser/ash/input_method/stub_input_method_engine_observer.h
@@ -41,6 +41,7 @@
                                 int offset) override {}
   void OnCompositionBoundsChanged(
       const std::vector<gfx::Rect>& bounds) override {}
+  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override {}
   void OnScreenProjectionChanged(bool is_projected) override {}
   void OnReset(const std::string& engine_id) override {}
   void OnSuggestionsChanged(
diff --git a/chrome/browser/ash/login/help_app_launcher.cc b/chrome/browser/ash/login/help_app_launcher.cc
index e4bda34..e9678c5 100644
--- a/chrome/browser/ash/login/help_app_launcher.cc
+++ b/chrome/browser/ash/login/help_app_launcher.cc
@@ -77,7 +77,7 @@
                                           const GURL& topic_url) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   LoginWebDialog* dialog = new LoginWebDialog(
-      profile, NULL, parent_window_,
+      profile, parent_window_,
       l10n_util::GetStringUTF16(IDS_LOGIN_OOBE_HELP_DIALOG_TITLE), topic_url);
   dialog->Show();
   // The dialog object will be deleted on dialog close.
diff --git a/chrome/browser/ash/login/screens/enable_debugging_screen.cc b/chrome/browser/ash/login/screens/enable_debugging_screen.cc
index 9d3ff094..bdc17cb7 100644
--- a/chrome/browser/ash/login/screens/enable_debugging_screen.cc
+++ b/chrome/browser/ash/login/screens/enable_debugging_screen.cc
@@ -89,8 +89,8 @@
   LoginWebDialog* dialog = new LoginWebDialog(
       Profile::FromWebUI(
           LoginDisplayHost::default_host()->GetOobeUI()->web_ui()),
-      nullptr, LoginDisplayHost::default_host()->GetNativeWindow(),
-      std::u16string(), data_url);
+      LoginDisplayHost::default_host()->GetNativeWindow(), std::u16string(),
+      data_url);
   dialog->Show();
 }
 
diff --git a/chrome/browser/ash/login/screens/error_screen.cc b/chrome/browser/ash/login/screens/error_screen.cc
index 3c05d38..8f58bd5 100644
--- a/chrome/browser/ash/login/screens/error_screen.cc
+++ b/chrome/browser/ash/login/screens/error_screen.cc
@@ -94,14 +94,10 @@
   network_state_informer_ = new NetworkStateInformer();
   network_state_informer_->Init();
   NetworkHandler::Get()->network_connection_handler()->AddObserver(this);
-  if (view_)
-    view_->Bind(this);
 }
 
 ErrorScreen::~ErrorScreen() {
   NetworkHandler::Get()->network_connection_handler()->RemoveObserver(this);
-  if (view_)
-    view_->Unbind();
 }
 
 void ErrorScreen::AllowGuestSignin(bool allowed) {
@@ -208,23 +204,6 @@
   }
 }
 
-void ErrorScreen::DoShow() {
-  LOG(WARNING) << "Network error screen message is shown";
-  session_manager::SessionManager::Get()->NotifyNetworkErrorScreenShown();
-  network_portal_detector::GetInstance()->SetStrategy(
-      PortalDetectorStrategy::STRATEGY_ID_ERROR_SCREEN);
-}
-
-void ErrorScreen::DoHide() {
-  LOG(WARNING) << "Network error screen message is hidden";
-  if (on_hide_callback_) {
-    std::move(on_hide_callback_).Run();
-    on_hide_callback_ = base::OnceClosure();
-  }
-  network_portal_detector::GetInstance()->SetStrategy(
-      PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN);
-}
-
 void ErrorScreen::ShowNetworkErrorMessage(NetworkStateInformer::State state,
                                           NetworkError::ErrorReason reason) {
   const std::string network_path = network_state_informer_->network_path();
@@ -272,13 +251,27 @@
     SetHideCallback(base::BindOnce(&ErrorScreen::DefaultHideCallback,
                                    weak_factory_.GetWeakPtr()));
   }
-  if (view_)
-    view_->Show();
+  if (!view_)
+    return;
+
+  view_->Show();
+  LOG(WARNING) << "Network error screen message is shown";
+  session_manager::SessionManager::Get()->NotifyNetworkErrorScreenShown();
+  network_portal_detector::GetInstance()->SetStrategy(
+      PortalDetectorStrategy::STRATEGY_ID_ERROR_SCREEN);
 }
 
 void ErrorScreen::HideImpl() {
-  if (view_ && !is_hidden())
-    view_->Hide();
+  if (!view_ || is_hidden())
+    return;
+
+  LOG(WARNING) << "Network error screen message is hidden";
+  if (on_hide_callback_) {
+    std::move(on_hide_callback_).Run();
+    on_hide_callback_ = base::OnceClosure();
+  }
+  network_portal_detector::GetInstance()->SetStrategy(
+      PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN);
 }
 
 void ErrorScreen::OnUserAction(const base::Value::List& args) {
@@ -363,7 +356,7 @@
   gfx::NativeWindow native_window =
       LoginDisplayHost::default_host()->GetNativeWindow();
   CertificateManagerDialog* dialog =
-      new CertificateManagerDialog(GetAppProfile(), NULL, native_window);
+      new CertificateManagerDialog(GetAppProfile(), native_window);
   dialog->Show();
 }
 
diff --git a/chrome/browser/ash/login/screens/error_screen.h b/chrome/browser/ash/login/screens/error_screen.h
index 834358a..45c0eb7 100644
--- a/chrome/browser/ash/login/screens/error_screen.h
+++ b/chrome/browser/ash/login/screens/error_screen.h
@@ -96,12 +96,6 @@
   // been created.
   void MaybeInitCaptivePortalWindowProxy(content::WebContents* web_contents);
 
-  // Actually show or hide the screen. These are called by ErrorScreenHandler;
-  // having two show methods (Show/Hide from BaseScreen below) is confusing
-  // and this should be cleaned up.
-  void DoShow();
-  void DoHide();
-
   void ShowNetworkErrorMessage(NetworkStateInformer::State state,
                                NetworkError::ErrorReason reason);
 
diff --git a/chrome/browser/ash/login/screens/mock_error_screen.cc b/chrome/browser/ash/login/screens/mock_error_screen.cc
index 377416a..b88376b6 100644
--- a/chrome/browser/ash/login/screens/mock_error_screen.cc
+++ b/chrome/browser/ash/login/screens/mock_error_screen.cc
@@ -30,21 +30,8 @@
   MockSetErrorState(error_state, network);
 }
 
-MockErrorScreenView::MockErrorScreenView() {
-  EXPECT_CALL(*this, MockBind(_)).Times(AtLeast(1));
-  EXPECT_CALL(*this, MockUnbind()).Times(AtLeast(1));
-}
+MockErrorScreenView::MockErrorScreenView() = default;
 
 MockErrorScreenView::~MockErrorScreenView() = default;
 
-void MockErrorScreenView::Bind(ErrorScreen* screen) {
-  screen_ = screen;
-  MockBind(screen);
-}
-
-void MockErrorScreenView::Unbind() {
-  screen_ = nullptr;
-  MockUnbind();
-}
-
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/mock_error_screen.h b/chrome/browser/ash/login/screens/mock_error_screen.h
index 6e0047a..17ffbf5 100644
--- a/chrome/browser/ash/login/screens/mock_error_screen.h
+++ b/chrome/browser/ash/login/screens/mock_error_screen.h
@@ -34,13 +34,8 @@
   MockErrorScreenView();
   virtual ~MockErrorScreenView();
 
-  void Bind(ErrorScreen* screen) override;
-  void Unbind() override;
-
   MOCK_METHOD0(Show, void());
   MOCK_METHOD0(Hide, void());
-  MOCK_METHOD1(MockBind, void(ErrorScreen* screen));
-  MOCK_METHOD0(MockUnbind, void());
   MOCK_METHOD1(ShowOobeScreen, void(OobeScreenId screen));
   MOCK_METHOD1(SetErrorStateCode, void(NetworkError::ErrorState error_state));
   MOCK_METHOD1(SetErrorStateNetwork, void(const std::string& network_name));
@@ -50,9 +45,6 @@
   MOCK_METHOD1(SetUIState, void(NetworkError::UIState ui_state));
   MOCK_METHOD1(SetIsPersistentError, void(bool is_persistent));
   MOCK_METHOD0(OnCancelButtonClicked, void());
-
- private:
-  ErrorScreen* screen_ = nullptr;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
index 551aff3..9a79869 100644
--- a/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/theme_selection_screen_browsertest.cc
@@ -7,6 +7,8 @@
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/login_screen_test_api.h"
 #include "ash/public/cpp/style/color_provider.h"
+#include "ash/shell.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "chrome/browser/ash/login/screens/guest_tos_screen.h"
 #include "chrome/browser/ash/login/screens/welcome_screen.h"
 #include "chrome/browser/ash/login/test/fake_eula_mixin.h"
@@ -35,6 +37,10 @@
                                             kLightThemeButton};
 const test::UIPath kAutoThemeButtonPath = {kThemeSelectionId, kAutoThemeButton};
 const test::UIPath kNextButtonPath = {kThemeSelectionId, "nextButton"};
+const test::UIPath kScreenSubtitleClamshellPath = {
+    kThemeSelectionId, "theme-selection-subtitle-clamshell"};
+const test::UIPath kScreenSubtitleTabletPath = {
+    kThemeSelectionId, "theme-selection-subtitle-tablet"};
 }  // namespace
 
 class ThemeSelectionScreenTest
@@ -72,6 +78,12 @@
     run_loop.Run();
   }
 
+  void setTabletMode(bool enabled) {
+    ash::TabletMode::Waiter waiter(enabled);
+    ash::Shell::Get()->tablet_mode_controller()->SetEnabledForTest(enabled);
+    waiter.Wait();
+  }
+
   absl::optional<ThemeSelectionScreen::Result> result_;
 
  protected:
@@ -120,6 +132,20 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(ThemeSelectionScreenTest, ToggleTabletMode) {
+  ShowThemeSelectionScreen();
+  // by default clamshell mode is used
+  test::OobeJS().ExpectVisiblePath(kScreenSubtitleClamshellPath);
+
+  // switch to tablet mode
+  setTabletMode(true);
+  test::OobeJS().ExpectVisiblePath(kScreenSubtitleTabletPath);
+
+  // and back to clamshell
+  setTabletMode(false);
+  test::OobeJS().ExpectVisiblePath(kScreenSubtitleClamshellPath);
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          ThemeSelectionScreenTest,
                          ::testing::Values(kDarkThemeButtonPath,
diff --git a/chrome/browser/ash/login/ui/login_web_dialog.cc b/chrome/browser/ash/login/ui/login_web_dialog.cc
index 7bb91fa6..85bb6e0 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog.cc
+++ b/chrome/browser/ash/login/ui/login_web_dialog.cc
@@ -44,16 +44,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 // LoginWebDialog, public:
 
-void LoginWebDialog::Delegate::OnDialogClosed() {}
-
 LoginWebDialog::LoginWebDialog(content::BrowserContext* browser_context,
-                               Delegate* delegate,
                                gfx::NativeWindow parent_window,
                                const std::u16string& title,
                                const GURL& url)
     : browser_context_(browser_context),
       parent_window_(parent_window),
-      delegate_(delegate),
       title_(title),
       url_(url) {
   if (!parent_window_ && LoginDisplayHost::default_host())
@@ -124,8 +120,6 @@
 
 void LoginWebDialog::OnDialogClosed(const std::string& json_retval) {
   dialog_window_ = nullptr;
-  if (delegate_)
-    delegate_->OnDialogClosed();
   delete this;
 }
 
diff --git a/chrome/browser/ash/login/ui/login_web_dialog.h b/chrome/browser/ash/login/ui/login_web_dialog.h
index c815844f..a5e1fbd 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog.h
+++ b/chrome/browser/ash/login/ui/login_web_dialog.h
@@ -22,21 +22,9 @@
 // Launches web dialog during OOBE/Login with specified URL and title.
 class LoginWebDialog : public ui::WebDialogDelegate {
  public:
-  // Delegate class to get notifications from the dialog.
-  // TODO(https://crbug.com/1338520): It isn't used anywhere. Remove.
-  class Delegate {
-   public:
-    // Called when dialog has been closed.
-    virtual void OnDialogClosed();
-
-   protected:
-    virtual ~Delegate() {}
-  };
-
   // If `parent_window` is null then the dialog is placed in the modal dialog
   // container on the primary display.
   LoginWebDialog(content::BrowserContext* browser_context,
-                 Delegate* delegate,
                  gfx::NativeWindow parent_window,
                  const std::u16string& title,
                  const GURL& url);
@@ -88,8 +76,6 @@
   content::BrowserContext* const browser_context_;
   gfx::NativeWindow parent_window_;
   gfx::NativeWindow dialog_window_;
-  // Notifications receiver.
-  Delegate* const delegate_;
 
   std::u16string title_;
   const GURL url_;
diff --git a/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc b/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc
index 022ae2f5..f17633f 100644
--- a/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc
+++ b/chrome/browser/ash/login/ui/login_web_dialog_browsertest.cc
@@ -21,7 +21,7 @@
 // Tests that LoginWebDialog is not minimizable.
 IN_PROC_BROWSER_TEST_F(LoginWebDialogTest, CannotMinimize) {
   LoginWebDialog* dialog = new LoginWebDialog(
-      browser()->profile(), nullptr, browser()->window()->GetNativeWindow(),
+      browser()->profile(), browser()->window()->GetNativeWindow(),
       std::u16string(), GURL());
   dialog->Show();
   aura::Window* window = dialog->get_dialog_window_for_test();
@@ -33,7 +33,7 @@
 // Tests that LoginWebDialog can be closed by 'Shift + BrowserBack' accelerator.
 IN_PROC_BROWSER_TEST_F(LoginWebDialogTest, CloseDialogByAccelerator) {
   LoginWebDialog* dialog = new LoginWebDialog(
-      browser()->profile(), nullptr, browser()->window()->GetNativeWindow(),
+      browser()->profile(), browser()->window()->GetNativeWindow(),
       std::u16string(), GURL());
   dialog->Show();
   gfx::NativeWindow window = dialog->get_dialog_window_for_test();
@@ -47,8 +47,8 @@
 
 // Tests that LoginWebDialog does not crash with missing parent window.
 IN_PROC_BROWSER_TEST_F(LoginWebDialogTest, NoParentWindow) {
-  LoginWebDialog* dialog = new LoginWebDialog(
-      browser()->profile(), nullptr, nullptr, std::u16string(), GURL());
+  LoginWebDialog* dialog = new LoginWebDialog(browser()->profile(), nullptr,
+                                              std::u16string(), GURL());
   dialog->Show();
   aura::Window* window = dialog->get_dialog_window_for_test();
   ASSERT_TRUE(window);
diff --git a/chrome/browser/ash/note_taking_helper.cc b/chrome/browser/ash/note_taking_helper.cc
index 20d8355..0e45e36 100644
--- a/chrome/browser/ash/note_taking_helper.cc
+++ b/chrome/browser/ash/note_taking_helper.cc
@@ -213,7 +213,8 @@
   // They can just launch without the intent.
   if (has_note_taking_intent_filter) {
     apps::AppServiceProxyFactory::GetForProfile(profile)->LaunchAppWithIntent(
-        app_id, ui::EF_NONE, apps_util::CreateCreateNoteIntent(),
+        app_id, ui::EF_NONE,
+        ConvertIntentToMojomIntent(apps_util::CreateCreateNoteIntent()),
         apps::mojom::LaunchSource::kFromShelf);
   } else {
     apps::AppServiceProxyFactory::GetForProfile(profile)->Launch(
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_provider.cc b/chrome/browser/ash/policy/core/device_local_account_policy_provider.cc
index f2368c2..53450f8 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_provider.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_provider.cc
@@ -9,8 +9,6 @@
 #include "base/bind.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/external_data/device_local_account_external_data_manager.h"
-#include "chrome/browser/ash/settings/cros_settings.h"
-#include "chrome/browser/ui/webui/certificates_handler.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
@@ -166,48 +164,7 @@
                       base::Value(false), nullptr);
   }
 
-  bool device_restricted_managed_guest_session_enabled = false;
-  ash::CrosSettings::Get()->GetBoolean(
-      ash::kDeviceRestrictedManagedGuestSessionEnabled,
-      &device_restricted_managed_guest_session_enabled);
-  if (device_restricted_managed_guest_session_enabled) {
-    ApplyRestrictedManagedGuestSessionOverride(&chrome_policy);
-  }
-
   UpdatePolicy(std::move(bundle));
 }
 
-// Details about the restricted managed guest session and the overridden
-// policies can be found here: go/restricted-managed-guest-session.
-void DeviceLocalAccountPolicyProvider::
-    ApplyRestrictedManagedGuestSessionOverride(PolicyMap* chrome_policy) {
-  std::pair<std::string, base::Value> policy_overrides[] = {
-      {key::kPasswordManagerEnabled, base::Value(false)},
-      {key::kAllowDeletingBrowserHistory, base::Value(true)},
-      {key::kArcEnabled, base::Value(false)},
-      {key::kCrostiniAllowed, base::Value(false)},
-      {key::kUserPluginVmAllowed, base::Value(false)},
-      {key::kNetworkFileSharesAllowed, base::Value(false)},
-      {key::kCACertificateManagementAllowed,
-       base::Value(static_cast<int>(CACertificateManagementPermission::kNone))},
-      {key::kClientCertificateManagementAllowed,
-       base::Value(
-           static_cast<int>(ClientCertificateManagementPermission::kNone))},
-      {key::kEnableMediaRouter, base::Value(false)},
-      {key::kScreenCaptureAllowed, base::Value(false)},
-      {key::kKerberosEnabled, base::Value(false)},
-      {key::kUserBorealisAllowed, base::Value(false)},
-      {key::kDeletePrintJobHistoryAllowed, base::Value(true)},
-      {key::kLacrosSecondaryProfilesAllowed, base::Value(false)},
-      {key::kLacrosAvailability, base::Value("lacros_disallowed")},
-  };
-
-  for (auto& policy_override : policy_overrides) {
-    chrome_policy->Set(policy_override.first, POLICY_LEVEL_MANDATORY,
-                       POLICY_SCOPE_USER,
-                       POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-                       std::move(policy_override.second), nullptr);
-  }
-}
-
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_provider.h b/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
index 93f484f2..4595514 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_provider.h
@@ -69,10 +69,6 @@
   // policy from the broker if available or keeping the current policy.
   void UpdateFromBroker();
 
-  // Applies the policy restrictions related to the restricted managed guest
-  // session override.
-  void ApplyRestrictedManagedGuestSessionOverride(PolicyMap* chrome_policy);
-
   const std::string user_id_;
   scoped_refptr<DeviceLocalAccountExternalDataManager> external_data_manager_;
 
diff --git a/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc b/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
index d52d1a3..b45ac2e 100644
--- a/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_policy_service_unittest.cc
@@ -1068,128 +1068,6 @@
   Mock::VerifyAndClearExpectations(&provider_observer_);
 }
 
-TEST_F(DeviceLocalAccountPolicyProviderTest,
-       DeviceRestrictedManagedGuestSessionEnabled) {
-  EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
-      .Times(AtLeast(1));
-  InstallDeviceLocalAccountPolicy(kAccount1);
-  AddDeviceLocalAccountToPolicy(kAccount1);
-  InstallDevicePolicy();
-  Mock::VerifyAndClearExpectations(&provider_observer_);
-  DeviceLocalAccountPolicyBroker* broker =
-      service_->GetBrokerForUser(account_1_user_id_);
-  ASSERT_TRUE(broker);
-
-  // Disabled DeviceRestrictedManagedGuestSessionEnabled policy does not
-  // change other policy values.
-  EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
-      .Times(AtLeast(1));
-  cros_settings_helper_->SetBoolean(
-      ash::kDeviceRestrictedManagedGuestSessionEnabled, false);
-  InstallDeviceLocalAccountPolicy(kAccount1);
-  broker->core()->store()->Load();
-  FlushDeviceSettings();
-  Mock::VerifyAndClearExpectations(&provider_observer_);
-
-  PolicyBundle expected_policy_bundle;
-  expected_policy_bundle.Get(PolicyNamespace(
-      POLICY_DOMAIN_CHROME, std::string())) = expected_policy_map_.Clone();
-  EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
-
-  // Enabled DeviceRestrictedManagedGuestSessionEnabled policy overrides
-  // certain policies.
-  EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
-      .Times(AtLeast(1));
-  cros_settings_helper_->SetBoolean(
-      ash::kDeviceRestrictedManagedGuestSessionEnabled, true);
-  device_local_account_policy_.payload()
-      .mutable_passwordmanagerenabled()
-      ->set_value(true);
-  device_local_account_policy_.payload()
-      .mutable_allowdeletingbrowserhistory()
-      ->set_value(false);
-  device_local_account_policy_.payload().mutable_arcenabled()->set_value(true);
-  InstallDeviceLocalAccountPolicy(kAccount1);
-  broker->core()->store()->Load();
-  FlushDeviceSettings();
-  Mock::VerifyAndClearExpectations(&provider_observer_);
-
-  PolicyMap expected_policy_map_restricted = expected_policy_map_.Clone();
-  expected_policy_map_restricted.Set(
-      key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kAllowDeletingBrowserHistory, POLICY_LEVEL_MANDATORY,
-      POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(true), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kArcEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kCrostiniAllowed, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kUserPluginVmAllowed, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kNetworkFileSharesAllowed, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kCACertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
-      POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(static_cast<int>(CACertificateManagementPermission::kNone)),
-      nullptr);
-  expected_policy_map_restricted.Set(
-      key::kClientCertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
-      POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(
-          static_cast<int>(ClientCertificateManagementPermission::kNone)),
-      nullptr);
-  expected_policy_map_restricted.Set(
-      key::kEnableMediaRouter, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kScreenCaptureAllowed, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kKerberosEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kUserBorealisAllowed, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kDeletePrintJobHistoryAllowed, POLICY_LEVEL_MANDATORY,
-      POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(true), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kLacrosSecondaryProfilesAllowed, POLICY_LEVEL_MANDATORY,
-      POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value(false), nullptr);
-  expected_policy_map_restricted.Set(
-      key::kLacrosAvailability, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
-      base::Value("lacros_disallowed"), nullptr);
-
-  expected_policy_bundle.Get(
-      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())) =
-      expected_policy_map_restricted.Clone();
-  EXPECT_TRUE(expected_policy_bundle.Equals(provider_->policies()));
-}
-
 TEST_F(DeviceLocalAccountPolicyProviderKioskTest, WebKioskPolicy) {
   EXPECT_CALL(provider_observer_, OnUpdatePolicy(provider_.get()))
       .Times(AtLeast(1));
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl_unittest.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl_unittest.cc
index e9014ab..a9c3f71 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl_unittest.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl_unittest.cc
@@ -82,58 +82,27 @@
 
 enum class AutoEnrollmentProtocol { kFRE = 0, kInitialEnrollment = 1 };
 
-enum class PsmState { kEnabled = 0, kDisabled = 1 };
-
-// Holds the state of the AutoEnrollmentClientImplTest and its subclass i.e.
-// PsmHelperTest. It will be used to run their tests with different values.
-struct AutoEnrollmentClientImplTestState final {
-  AutoEnrollmentClientImplTestState(
-      AutoEnrollmentProtocol auto_enrollment_protocol,
-      PsmState psm_state)
-      : auto_enrollment_protocol(auto_enrollment_protocol),
-        psm_state(psm_state) {}
-
-  AutoEnrollmentProtocol auto_enrollment_protocol;
-  PsmState psm_state;
-};
-
-class AutoEnrollmentClientImplTest
-    : public testing::TestWithParam<AutoEnrollmentClientImplTestState> {
+class AutoEnrollmentClientImplBaseTest : public testing::Test {
  public:
-  AutoEnrollmentClientImplTest(const AutoEnrollmentClientImplTest&) = delete;
-  AutoEnrollmentClientImplTest& operator=(const AutoEnrollmentClientImplTest&) =
+  AutoEnrollmentClientImplBaseTest(const AutoEnrollmentClientImplBaseTest&) =
       delete;
+  AutoEnrollmentClientImplBaseTest& operator=(
+      const AutoEnrollmentClientImplBaseTest&) = delete;
 
  protected:
-  AutoEnrollmentClientImplTest()
+  explicit AutoEnrollmentClientImplBaseTest(AutoEnrollmentProtocol protocol)
       : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
         local_state_(scoped_testing_local_state_.Get()),
-        state_(AUTO_ENROLLMENT_STATE_PENDING) {}
-
-  void SetUp() override {
+        state_(AUTO_ENROLLMENT_STATE_PENDING),
+        protocol_(protocol) {
     CreateClient(kPowerStart, kPowerLimit);
-    ASSERT_FALSE(local_state_->GetUserPref(prefs::kShouldAutoEnroll));
-    ASSERT_FALSE(local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit));
   }
 
-  void TearDown() override {
+  ~AutoEnrollmentClientImplBaseTest() override {
     // Flush any deletion tasks.
     base::RunLoop().RunUntilIdle();
   }
 
-  AutoEnrollmentProtocol GetAutoEnrollmentProtocol() const {
-    return GetParam().auto_enrollment_protocol;
-  }
-
-  PsmState GetPsmState() const { return GetParam().psm_state; }
-
-  std::string GetAutoEnrollmentProtocolUmaSuffix() const {
-    return GetAutoEnrollmentProtocol() ==
-                   AutoEnrollmentProtocol::kInitialEnrollment
-               ? kUMASuffixInitialEnrollment
-               : kUMASuffixFRE;
-  }
-
   void CreateClient(int power_initial, int power_limit) {
     state_ = AUTO_ENROLLMENT_STATE_PENDING;
     service_ =
@@ -142,21 +111,17 @@
     base::RunLoop().RunUntilIdle();
 
     auto progress_callback =
-        base::BindRepeating(&AutoEnrollmentClientImplTest::ProgressCallback,
+        base::BindRepeating(&AutoEnrollmentClientImplBaseTest::ProgressCallback,
                             base::Unretained(this));
     shared_url_loader_factory_ =
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
             &url_loader_factory_);
 
-    if (GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE) {
+    if (protocol_ == AutoEnrollmentProtocol::kFRE) {
       client_ = AutoEnrollmentClientImpl::FactoryImpl().CreateForFRE(
           progress_callback, service_.get(), local_state_,
           shared_url_loader_factory_, kStateKey, power_initial, power_limit);
     } else {
-      // PSM has to be enabled whenever creating a client for initial
-      // enrollment.
-      DCHECK_EQ(GetPsmState(), PsmState::kEnabled);
-
       // Store a non-owned smart pointer of FakePsmRlweDmserverClient in
       // `fake_psm_rlwe_dmserver_client_ptr_`.
       auto fake_psm_rlwe_dmserver_client =
@@ -183,21 +148,6 @@
         .RetiresOnSaturation();
   }
 
-  void ServerWillReply(int64_t modulus, bool with_hashes, bool with_id_hash) {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    ASSERT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    em::DeviceManagementResponse response =
-        GetAutoEnrollmentResponse(modulus, with_hashes, with_id_hash);
-
-    EXPECT_CALL(job_creation_handler_, OnJobCreation)
-        .WillOnce(DoAll(service_->CaptureJobType(&auto_enrollment_job_type_),
-                        service_->CaptureRequest(&last_request_),
-                        service_->SendJobOKAsync(response)))
-        .RetiresOnSaturation();
-  }
-
   em::DeviceInitialEnrollmentStateResponse::InitialEnrollmentMode
   MapRestoreModeToInitialEnrollmentMode(
       em::DeviceStateRetrievalResponse::RestoreMode restore_mode) {
@@ -238,7 +188,7 @@
       bool is_license_packaged_with_device,
       em::DeviceInitialEnrollmentStateResponse::LicensePackagingSKU
           license_sku) {
-    if (GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE) {
+    if (protocol_ == AutoEnrollmentProtocol::kFRE) {
       ServerWillSendStateForFRE(management_domain, restore_mode,
                                 device_disabled_message, absl::nullopt);
     } else {
@@ -248,15 +198,6 @@
     }
   }
 
-  DeviceManagementService::JobConfiguration::JobType
-  GetStateRetrievalJobType() {
-    return GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE
-               ? DeviceManagementService::JobConfiguration::
-                     TYPE_DEVICE_STATE_RETRIEVAL
-               : DeviceManagementService::JobConfiguration::
-                     TYPE_INITIAL_ENROLLMENT_STATE_RETRIEVAL;
-  }
-
   void ServerWillSendStateForFRE(
       const std::string& management_domain,
       em::DeviceStateRetrievalResponse::RestoreMode restore_mode,
@@ -311,7 +252,7 @@
 
   DeviceManagementService::JobConfiguration::JobType
   GetExpectedStateRetrievalJobType() {
-    return GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE
+    return protocol_ == AutoEnrollmentProtocol::kFRE
                ? DeviceManagementService::JobConfiguration::
                      TYPE_DEVICE_STATE_RETRIEVAL
                : DeviceManagementService::JobConfiguration::
@@ -324,43 +265,12 @@
                         SaveArg<0>(job)));
   }
 
-  void ServerReplyAsyncJobWithAutoEnrollmentResponse(
-      int64_t modulus,
-      bool with_hashes,
-      bool with_id_hash,
-      DeviceManagementService::JobForTesting* job) {
-    em::DeviceManagementResponse response =
-        GetAutoEnrollmentResponse(modulus, with_hashes, with_id_hash);
-    service_->SendJobOKNow(job, response);
-  }
-
   void ServerRepliesEmptyResponseForAsyncJob(
       DeviceManagementService::JobForTesting* job) {
     em::DeviceManagementResponse dummy_response;
     service_->SendJobOKNow(job, dummy_response);
   }
 
-  bool HasCachedDecision() {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    return local_state_->GetUserPref(prefs::kShouldAutoEnroll);
-  }
-
-  void VerifyCachedResult(bool should_enroll, int power_limit) {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    base::Value value_should_enroll(should_enroll);
-    base::Value value_power_limit(power_limit);
-    EXPECT_EQ(value_should_enroll,
-              *local_state_->GetUserPref(prefs::kShouldAutoEnroll));
-    EXPECT_EQ(value_power_limit,
-              *local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit));
-  }
-
   bool HasServerBackedState() {
     return local_state_->GetUserPref(prefs::kServerBackedDeviceState);
   }
@@ -370,7 +280,7 @@
                                const std::string& expected_disabled_message,
                                bool expected_is_license_packaged_with_device,
                                const std::string& expected_license_type) {
-    if (GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE) {
+    if (protocol_ == AutoEnrollmentProtocol::kFRE) {
       VerifyServerBackedStateForFRE(expected_management_domain,
                                     expected_restore_mode,
                                     expected_disabled_message);
@@ -419,7 +329,7 @@
       const std::string* actual_restore_mode =
           state_dict->FindStringKey(kDeviceStateMode);
       EXPECT_TRUE(actual_restore_mode);
-      EXPECT_EQ(GetAutoEnrollmentProtocol() == AutoEnrollmentProtocol::kFRE
+      EXPECT_EQ(protocol_ == AutoEnrollmentProtocol::kFRE
                     ? expected_restore_mode
                     : MapDeviceRestoreStateToDeviceInitialState(
                           expected_restore_mode),
@@ -461,51 +371,6 @@
     EXPECT_EQ(*actual_license_type, expected_license_type);
   }
 
-  // Expects one sample for |kUMAHashDanceNetworkErrorCode| which has value of
-  // |network_error|.
-  void ExpectHashDanceNetworkErrorHistogram(int network_error) const {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    histogram_tester_.ExpectBucketCount(
-        kUMAHashDanceNetworkErrorCode + GetAutoEnrollmentProtocolUmaSuffix(),
-        network_error, /*expected_count=*/1);
-  }
-
-  // Expects a sample for |kUMAHashDanceRequestStatus| with count
-  // |dm_status_count|.
-  void ExpectHashDanceRequestStatusHistogram(DeviceManagementStatus dm_status,
-                                             int dm_status_count) const {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    histogram_tester_.ExpectBucketCount(
-        kUMAHashDanceRequestStatus + GetAutoEnrollmentProtocolUmaSuffix(),
-        dm_status, dm_status_count);
-  }
-
-  // Expects a sample for |kUMAHashDanceProtocolTime| to have value
-  // |expected_time_recorded|.
-  // if |success_time_recorded| is true it expects one sample for
-  // |kUMAHashDanceSuccessTime| to have value |expected_time_recorded|.
-  // Otherwise, expects no sample for |kUMAHashDanceSuccessTime|.
-  void ExpectHashDanceExecutionTimeHistogram(
-      base::TimeDelta expected_time_recorded,
-      bool success_time_recorded) const {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
-    histogram_tester_.ExpectUniqueTimeSample(
-        kUMAHashDanceProtocolTime + GetAutoEnrollmentProtocolUmaSuffix(),
-        expected_time_recorded, /*expected_count=*/1);
-    histogram_tester_.ExpectUniqueTimeSample(
-        kUMAHashDanceSuccessTime + GetAutoEnrollmentProtocolUmaSuffix(),
-        expected_time_recorded, success_time_recorded ? 1 : 0);
-  }
-
   const em::DeviceAutoEnrollmentRequest& auto_enrollment_request() {
     return last_request_.auto_enrollment_request();
   }
@@ -536,8 +401,6 @@
       DeviceManagementService::JobConfiguration::TYPE_INVALID;
   DeviceManagementService::JobConfiguration::JobType last_async_job_type_ =
       DeviceManagementService::JobConfiguration::TYPE_INVALID;
-  DeviceManagementService::JobConfiguration::JobType auto_enrollment_job_type_ =
-      DeviceManagementService::JobConfiguration::TYPE_INVALID;
   DeviceManagementService::JobConfiguration::JobType state_retrieval_job_type_ =
       DeviceManagementService::JobConfiguration::TYPE_INVALID;
 
@@ -546,14 +409,108 @@
       nullptr;
 
  private:
+  const AutoEnrollmentProtocol protocol_;
+  network::TestURLLoaderFactory url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
+  std::unique_ptr<AutoEnrollmentClient> client_;
+};
+
+class AutoEnrollmentClientImplTest : public AutoEnrollmentClientImplBaseTest {
+ protected:
+  AutoEnrollmentClientImplTest()
+      : AutoEnrollmentClientImplBaseTest(AutoEnrollmentProtocol::kFRE) {}
+
+  void SetUp() override {
+    ASSERT_FALSE(local_state_->GetUserPref(prefs::kShouldAutoEnroll));
+    ASSERT_FALSE(local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit));
+
+    AutoEnrollmentClientImplBaseTest::SetUp();
+  }
+
+  void ServerWillReply(int64_t modulus, bool with_hashes, bool with_id_hash) {
+    em::DeviceManagementResponse response =
+        GetAutoEnrollmentResponse(modulus, with_hashes, with_id_hash);
+
+    EXPECT_CALL(job_creation_handler_, OnJobCreation)
+        .WillOnce(DoAll(service_->CaptureJobType(&auto_enrollment_job_type_),
+                        service_->CaptureRequest(&last_request_),
+                        service_->SendJobOKAsync(response)))
+        .RetiresOnSaturation();
+  }
+
+  void ServerReplyAsyncJobWithAutoEnrollmentResponse(
+      int64_t modulus,
+      bool with_hashes,
+      bool with_id_hash,
+      DeviceManagementService::JobForTesting* job) {
+    em::DeviceManagementResponse response =
+        GetAutoEnrollmentResponse(modulus, with_hashes, with_id_hash);
+    service_->SendJobOKNow(job, response);
+  }
+
+  bool HasCachedDecision() {
+    return local_state_->GetUserPref(prefs::kShouldAutoEnroll);
+  }
+
+  void VerifyCachedResult(bool should_enroll, int power_limit) {
+    base::Value value_should_enroll(should_enroll);
+    base::Value value_power_limit(power_limit);
+    EXPECT_EQ(value_should_enroll,
+              *local_state_->GetUserPref(prefs::kShouldAutoEnroll));
+    EXPECT_EQ(value_power_limit,
+              *local_state_->GetUserPref(prefs::kAutoEnrollmentPowerLimit));
+  }
+
+  // Expects one sample for |kUMAHashDanceNetworkErrorCode| which has value of
+  // |network_error|.
+  void ExpectHashDanceNetworkErrorHistogram(int network_error) const {
+    histogram_tester_.ExpectBucketCount(
+        std::string(kUMAHashDanceNetworkErrorCode) + kUMASuffixFRE,
+        network_error, /*expected_count=*/1);
+  }
+
+  // Expects a sample for |kUMAHashDanceRequestStatus| with count
+  // |dm_status_count|.
+  void ExpectHashDanceRequestStatusHistogram(DeviceManagementStatus dm_status,
+                                             int dm_status_count) const {
+    histogram_tester_.ExpectBucketCount(
+        std::string(kUMAHashDanceRequestStatus) + kUMASuffixFRE, dm_status,
+        dm_status_count);
+  }
+
+  // Expects a sample for |kUMAHashDanceProtocolTime| to have value
+  // |expected_time_recorded|.
+  // if |success_time_recorded| is true it expects one sample for
+  // |kUMAHashDanceSuccessTime| to have value |expected_time_recorded|.
+  // Otherwise, expects no sample for |kUMAHashDanceSuccessTime|.
+  void ExpectHashDanceExecutionTimeHistogram(
+      base::TimeDelta expected_time_recorded,
+      bool success_time_recorded) const {
+    histogram_tester_.ExpectUniqueTimeSample(
+        std::string(kUMAHashDanceProtocolTime) + kUMASuffixFRE,
+        expected_time_recorded, /*expected_bucket_count=*/1);
+    histogram_tester_.ExpectUniqueTimeSample(
+        std::string(kUMAHashDanceSuccessTime) + kUMASuffixFRE,
+        expected_time_recorded, success_time_recorded ? 1 : 0);
+  }
+
+  void ExpectHashDanceSyncExecutionTimeHistogram(bool success_time_recorded) {
+    // Note: The expected time is the difference between starting off the
+    // client, and finishing executing the protocol successfully. In this test,
+    // the protocol requests are synchronized. Then the recorded time will be
+    // zero.
+    ExpectHashDanceExecutionTimeHistogram(
+        /*expected_time_recorded=*/base::TimeDelta(), success_time_recorded);
+  }
+
+  DeviceManagementService::JobConfiguration::JobType auto_enrollment_job_type_ =
+      DeviceManagementService::JobConfiguration::TYPE_INVALID;
+
+ private:
   em::DeviceManagementResponse GetAutoEnrollmentResponse(
       int64_t modulus,
       bool with_hashes,
       bool with_id_hash) const {
-    // This method should be called only when the client has been created for
-    // FRE use case.
-    EXPECT_EQ(GetAutoEnrollmentProtocol(), AutoEnrollmentProtocol::kFRE);
-
     em::DeviceManagementResponse response;
     em::DeviceAutoEnrollmentResponse* enrollment_response =
         response.mutable_auto_enrollment_response();
@@ -573,13 +530,9 @@
 
     return response;
   }
-
-  network::TestURLLoaderFactory url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
-  std::unique_ptr<AutoEnrollmentClient> client_;
 };
 
-TEST_P(AutoEnrollmentClientImplTest, NetworkFailure) {
+TEST_F(AutoEnrollmentClientImplTest, NetworkFailure) {
   ServerWillFail(net::OK, DeviceManagementService::kServiceUnavailable);
   client()->Start();
   base::RunLoop().RunUntilIdle();
@@ -592,7 +545,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, EmptyReply) {
+TEST_F(AutoEnrollmentClientImplTest, EmptyReply) {
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
   client()->Start();
@@ -602,11 +555,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -616,7 +565,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ClientUploadsRightBits) {
+TEST_F(AutoEnrollmentClientImplTest, ClientUploadsRightBits) {
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
   client()->Start();
@@ -626,11 +575,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -645,7 +590,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForMoreThenFail) {
+TEST_F(AutoEnrollmentClientImplTest, AskForMoreThenFail) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/32, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -665,7 +610,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForMoreThenEvenMore) {
+TEST_F(AutoEnrollmentClientImplTest, AskForMoreThenEvenMore) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/32, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -678,11 +623,7 @@
 
   // Verify Hash dance protocol overall execution time histogram has been
   // recorded correctly. And its success time histogram has not been recorded.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/false);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -692,7 +633,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForLess) {
+TEST_F(AutoEnrollmentClientImplTest, AskForLess) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/8, /*with_hashes=*/false, /*with_id_hash=*/false);
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
@@ -708,11 +649,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -725,7 +662,7 @@
       kDisabledMessage, kWithLicense, kDeviceStateLicenseTypeEducation);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForSame) {
+TEST_F(AutoEnrollmentClientImplTest, AskForSame) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/16, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -742,11 +679,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -759,7 +692,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForSameTwice) {
+TEST_F(AutoEnrollmentClientImplTest, AskForSameTwice) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/16, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -772,11 +705,7 @@
 
   // Verify Hash dance protocol overall execution time histogram has been
   // recorded correctly. And its success time histogram has not been recorded.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/false);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -786,7 +715,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskForTooMuch) {
+TEST_F(AutoEnrollmentClientImplTest, AskForTooMuch) {
   ServerWillReply(/*modulus=*/512, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
   client()->Start();
@@ -796,11 +725,7 @@
 
   // Verify Hash dance protocol overall execution time histogram has been
   // recorded correctly. And its success time histogram has not been recorded.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/false);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -810,7 +735,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, AskNonPowerOf2) {
+TEST_F(AutoEnrollmentClientImplTest, AskNonPowerOf2) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/100, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -823,11 +748,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -841,7 +762,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ConsumerDevice) {
+TEST_F(AutoEnrollmentClientImplTest, ConsumerDevice) {
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/false);
   client()->Start();
   base::RunLoop().RunUntilIdle();
@@ -850,11 +771,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -870,7 +787,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ForcedReEnrollment) {
+TEST_F(AutoEnrollmentClientImplTest, ForcedReEnrollment) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState(
@@ -885,11 +802,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -908,7 +821,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ForcedReEnrollmentStateRetrivalfailure) {
+TEST_F(AutoEnrollmentClientImplTest, ForcedReEnrollmentStateRetrivalfailure) {
   InSequence sequence;
 
   const base::TimeDelta kOneSecondTimeDelta = base::Seconds(1);
@@ -972,7 +885,7 @@
   EXPECT_FALSE(device_state_job.IsActive());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ForcedEnrollmentZeroTouch) {
+TEST_F(AutoEnrollmentClientImplTest, ForcedEnrollmentZeroTouch) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState(
@@ -987,11 +900,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1010,7 +919,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_TRIGGER_ZERO_TOUCH);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, RequestedReEnrollment) {
+TEST_F(AutoEnrollmentClientImplTest, RequestedReEnrollment) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState(
@@ -1025,11 +934,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1042,7 +947,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, DeviceDisabled) {
+TEST_F(AutoEnrollmentClientImplTest, DeviceDisabled) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState("example.com",
@@ -1056,11 +961,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1072,7 +973,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, NoReEnrollment) {
+TEST_F(AutoEnrollmentClientImplTest, NoReEnrollment) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState(std::string(),
@@ -1086,11 +987,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1108,7 +1005,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, NoBitsUploaded) {
+TEST_F(AutoEnrollmentClientImplTest, NoBitsUploaded) {
   CreateClient(/*power_initial=*/0, /*power_limit=*/0);
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/false,
                   /*with_id_hash=*/false);
@@ -1119,11 +1016,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1137,7 +1030,7 @@
   EXPECT_FALSE(HasServerBackedState());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ManyBitsUploaded) {
+TEST_F(AutoEnrollmentClientImplTest, ManyBitsUploaded) {
   int64_t bottom62 = INT64_C(0x386e7244d097c3e6);
   for (int i = 0; i <= 62; ++i) {
     CreateClient(/*power_initial=*/i, /*power_limit=*/i);
@@ -1160,7 +1053,7 @@
   }
 }
 
-TEST_P(AutoEnrollmentClientImplTest, MoreThan32BitsUploaded) {
+TEST_F(AutoEnrollmentClientImplTest, MoreThan32BitsUploaded) {
   CreateClient(/*power_initial=*/10, /*power_limit=*/37);
   InSequence sequence;
   ServerWillReply(/*modulus=*/INT64_C(1) << 37, /*with_hashes=*/false,
@@ -1178,11 +1071,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1195,7 +1084,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, ReuseCachedDecision) {
+TEST_F(AutoEnrollmentClientImplTest, ReuseCachedDecision) {
   // No bucket download requests should be issued.
   EXPECT_CALL(job_creation_handler_, OnJobCreation).Times(0);
   local_state_->SetUserPref(prefs::kShouldAutoEnroll,
@@ -1223,7 +1112,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, RetryIfPowerLargerThanCached) {
+TEST_F(AutoEnrollmentClientImplTest, RetryIfPowerLargerThanCached) {
   local_state_->SetUserPref(prefs::kShouldAutoEnroll,
                             std::make_unique<base::Value>(false));
   local_state_->SetUserPref(prefs::kAutoEnrollmentPowerLimit,
@@ -1244,11 +1133,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(auto_enrollment_job_type_,
@@ -1260,7 +1145,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, NetworkChangeRetryAfterErrors) {
+TEST_F(AutoEnrollmentClientImplTest, NetworkChangeRetryAfterErrors) {
   ServerWillFail(net::OK, DeviceManagementService::kServiceUnavailable);
   client()->Start();
   base::RunLoop().RunUntilIdle();
@@ -1317,7 +1202,7 @@
                           kDisabledMessage, kNotWithLicense, kNoLicenseType);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, CancelAndDeleteSoonWithPendingRequest) {
+TEST_F(AutoEnrollmentClientImplTest, CancelAndDeleteSoonWithPendingRequest) {
   DeviceManagementService::JobForTesting job;
   ServerWillReplyAsync(&job);
   EXPECT_FALSE(job.IsActive());
@@ -1341,7 +1226,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_PENDING);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, NetworkChangedAfterCancelAndDeleteSoon) {
+TEST_F(AutoEnrollmentClientImplTest, NetworkChangedAfterCancelAndDeleteSoon) {
   DeviceManagementService::JobForTesting job;
   ServerWillReplyAsync(&job);
   EXPECT_FALSE(job.IsActive());
@@ -1378,7 +1263,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_PENDING);
 }
 
-TEST_P(AutoEnrollmentClientImplTest, CancelAndDeleteSoonAfterCompletion) {
+TEST_F(AutoEnrollmentClientImplTest, CancelAndDeleteSoonAfterCompletion) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
   ServerWillSendState(
@@ -1404,7 +1289,7 @@
   EXPECT_TRUE(base::CurrentThread::Get()->IsIdleForTesting());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, CancelAndDeleteSoonAfterNetworkFailure) {
+TEST_F(AutoEnrollmentClientImplTest, CancelAndDeleteSoonAfterNetworkFailure) {
   ServerWillFail(net::OK, DeviceManagementService::kServiceUnavailable);
   client()->Start();
   base::RunLoop().RunUntilIdle();
@@ -1422,7 +1307,7 @@
   EXPECT_TRUE(base::CurrentThread::Get()->IsIdleForTesting());
 }
 
-TEST_P(AutoEnrollmentClientImplTest, NetworkFailureThenRequireUpdatedModulus) {
+TEST_F(AutoEnrollmentClientImplTest, NetworkFailureThenRequireUpdatedModulus) {
   // This test verifies that if the first request fails due to a network
   // problem then the second request will correctly handle an updated
   // modulus request from the server.
@@ -1463,11 +1348,7 @@
 
   // Verify Hash dance protocol overall execution time and its success time
   // histograms were recorded correctly with the same value.
-  // Note: The expected time is the difference between starting off the client,
-  // and finishing executing the protocol successfully. In this test, the
-  // protocol requests are synchronized. Then the recorded time will be zero.
-  ExpectHashDanceExecutionTimeHistogram(
-      /*expected_time_recorded=*/base::TimeDelta(),
+  ExpectHashDanceSyncExecutionTimeHistogram(
       /*success_time_recorded=*/true);
 
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT);
@@ -1481,19 +1362,10 @@
   EXPECT_EQ(state_retrieval_job_type_, GetExpectedStateRetrievalJobType());
 }
 
-// PSM is disabed to test only Hash dance for FRE case extensively instead. That
-// is because PSM is running only for initial enrollment, and Hash dance for FRE
-// use case.
-INSTANTIATE_TEST_SUITE_P(FRE,
-                         AutoEnrollmentClientImplTest,
-                         testing::Values(AutoEnrollmentClientImplTestState(
-                             AutoEnrollmentProtocol::kFRE,
-                             PsmState::kDisabled)));
-
 using AutoEnrollmentClientImplFREToInitialEnrollmentTest =
     AutoEnrollmentClientImplTest;
 
-TEST_P(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
+TEST_F(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
        NoReEnrollmentInitialEnrollmentLicensePackaging) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
@@ -1525,7 +1397,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_NO_ENROLLMENT);
 }
 
-TEST_P(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
+TEST_F(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
        NoReEnrollmentInitialEnrollmentZeroTouch) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
@@ -1561,7 +1433,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_TRIGGER_ZERO_TOUCH);
 }
 
-TEST_P(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
+TEST_F(AutoEnrollmentClientImplFREToInitialEnrollmentTest,
        NoReEnrollmentInitialEnrollmentGuaranteed) {
   InSequence sequence;
   ServerWillReply(/*modulus=*/-1, /*with_hashes=*/true, /*with_id_hash=*/true);
@@ -1597,18 +1469,9 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_TRIGGER_ENROLLMENT);
 }
 
-// PSM is disabed to test only Hash dance for FRE case extensively instead. That
-// is because PSM is running only for initial enrollment, and Hash dance for FRE
-// use case.
-INSTANTIATE_TEST_SUITE_P(FREToInitialEnrollment,
-                         AutoEnrollmentClientImplFREToInitialEnrollmentTest,
-                         testing::Values(AutoEnrollmentClientImplTestState(
-                             AutoEnrollmentProtocol::kFRE,
-                             PsmState::kDisabled)));
-
 // This class is used to test PSM for initial enrollment test cases only.
 // Therefore, the PsmState param has to be kEnabled.
-class PsmHelperInitialEnrollmentTest : public AutoEnrollmentClientImplTest {
+class PsmHelperInitialEnrollmentTest : public AutoEnrollmentClientImplBaseTest {
  protected:
   // Indicates the state of the PSM protocol.
   enum class StateDiscoveryResult {
@@ -1622,16 +1485,11 @@
     kSuccessHasServerSideState = 2,
   };
 
-  PsmHelperInitialEnrollmentTest() {}
-  ~PsmHelperInitialEnrollmentTest() {
-    // Flush any deletion tasks.
-    base::RunLoop().RunUntilIdle();
-  }
+  PsmHelperInitialEnrollmentTest()
+      : AutoEnrollmentClientImplBaseTest(
+            AutoEnrollmentProtocol::kInitialEnrollment) {}
 
   void SetUp() override {
-    // Verify that PSM is enabled (i.e. PsmState has value kEnable).
-    ASSERT_EQ(GetPsmState(), PsmState::kEnabled);
-
     // Verify that all PSM prefs have not been set before.
     ASSERT_EQ(local_state_->GetUserPref(prefs::kShouldRetrieveDeviceState),
               nullptr);
@@ -1639,7 +1497,7 @@
               nullptr);
     ASSERT_EQ(local_state_->GetUserPref(prefs::kEnrollmentPsmResult), nullptr);
 
-    AutoEnrollmentClientImplTest::SetUp();
+    AutoEnrollmentClientImplBaseTest::SetUp();
   }
 
   void PsmWillReplyWith(PsmResult psm_result,
@@ -1690,7 +1548,7 @@
       const PsmHelperInitialEnrollmentTest&) = delete;
 };
 
-TEST_P(PsmHelperInitialEnrollmentTest,
+TEST_F(PsmHelperInitialEnrollmentTest,
        RetryLogicAfterNetworkFailureForRlweQueryResponse) {
   PsmWillReplyWith(PsmResult::kServerError);
 
@@ -1718,7 +1576,7 @@
   EXPECT_EQ(state_, AUTO_ENROLLMENT_STATE_SERVER_ERROR);
 }
 
-TEST_P(PsmHelperInitialEnrollmentTest,
+TEST_F(PsmHelperInitialEnrollmentTest,
        RetryLogicAfterMembershipSuccessfullyRetrieved) {
   const bool kExpectedMembershipResult = false;
   const base::TimeDelta kOneSecondTimeDelta = base::Seconds(1);
@@ -1774,7 +1632,7 @@
   }
 }
 
-TEST_P(PsmHelperInitialEnrollmentTest, PsmSucceedAndStateRetrievalSucceed) {
+TEST_F(PsmHelperInitialEnrollmentTest, PsmSucceedAndStateRetrievalSucceed) {
   const bool kExpectedMembershipResult = true;
   const base::TimeDelta kOneSecondTimeDelta = base::Seconds(1);
   const base::Time kExpectedPsmDeterminationTimestamp =
@@ -1824,7 +1682,7 @@
   }
 }
 
-TEST_P(PsmHelperInitialEnrollmentTest, PsmSucceedAndStateRetrievalFailed) {
+TEST_F(PsmHelperInitialEnrollmentTest, PsmSucceedAndStateRetrievalFailed) {
   const bool kExpectedMembershipResult = true;
   const base::TimeDelta kOneSecondTimeDelta = base::Seconds(1);
   const base::Time kExpectedPsmDeterminationTimestamp =
@@ -1865,14 +1723,5 @@
   }
 }
 
-// PSM is enabled to test initial enrollment case extensively only.
-// Note that: PSM is running only for initial enrollment, and Hash dance for
-// FRE use case.
-INSTANTIATE_TEST_SUITE_P(PsmForInitialEnrollment,
-                         PsmHelperInitialEnrollmentTest,
-                         testing::Values(AutoEnrollmentClientImplTestState(
-                             AutoEnrollmentProtocol::kInitialEnrollment,
-                             PsmState::kEnabled)));
-
 }  // namespace
 }  // namespace policy
diff --git a/chrome/browser/ash/remote_apps/remote_apps_manager.cc b/chrome/browser/ash/remote_apps/remote_apps_manager.cc
index cbd3a5c..94316ceb 100644
--- a/chrome/browser/ash/remote_apps/remote_apps_manager.cc
+++ b/chrome/browser/ash/remote_apps/remote_apps_manager.cc
@@ -354,7 +354,7 @@
 apps::mojom::MenuItemsPtr RemoteAppsManager::GetMenuModel(
     const std::string& id) {
   apps::mojom::MenuItemsPtr menu_items = apps::mojom::MenuItems::New();
-  // TODO(jityao): Temporary string for menu item.
+  // TODO(b/236785623): Temporary string for menu item.
   apps::AddCommandItem(ash::LAUNCH_NEW, IDS_APP_CONTEXT_MENU_ACTIVATE_ARC,
                        &menu_items);
   return menu_items;
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
index dbcd0f3..65c87133 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
@@ -20,6 +20,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_background_task.h"
 #include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/externally_managed_app_manager_impl.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_data_retriever.h"
 #include "chrome/browser/web_applications/test/fake_externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_database_factory.h"
@@ -198,7 +198,7 @@
     fake_externally_managed_app_manager_impl_ =
         std::make_unique<web_app::FakeExternallyManagedAppManager>(profile());
     test_system_web_app_manager_ =
-        std::make_unique<web_app::TestSystemWebAppManager>(profile());
+        std::make_unique<TestSystemWebAppManager>(profile());
     test_ui_manager_ = std::make_unique<web_app::FakeWebAppUiManager>();
     command_manager_ =
         std::make_unique<web_app::WebAppCommandManager>(profile());
@@ -275,7 +275,7 @@
     return *fake_externally_managed_app_manager_impl_;
   }
 
-  web_app::TestSystemWebAppManager& system_web_app_manager() {
+  TestSystemWebAppManager& system_web_app_manager() {
     return *test_system_web_app_manager_;
   }
 
@@ -341,8 +341,7 @@
   std::unique_ptr<web_app::WebAppInstallManager> install_manager_;
   std::unique_ptr<web_app::FakeExternallyManagedAppManager>
       fake_externally_managed_app_manager_impl_;
-  std::unique_ptr<web_app::TestSystemWebAppManager>
-      test_system_web_app_manager_;
+  std::unique_ptr<TestSystemWebAppManager> test_system_web_app_manager_;
   std::unique_ptr<web_app::FakeWebAppUiManager> test_ui_manager_;
   std::unique_ptr<web_app::WebAppCommandManager> command_manager_;
 };
@@ -1098,7 +1097,7 @@
       web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, AppUrl1())));
 
   auto unsynced_system_web_app_manager =
-      std::make_unique<web_app::TestSystemWebAppManager>(profile());
+      std::make_unique<TestSystemWebAppManager>(profile());
 
   unsynced_system_web_app_manager->SetSubsystems(
       &externally_managed_app_manager(), &controller().registrar(),
@@ -1517,7 +1516,7 @@
 
   // Creates a new SystemWebAppManager without the previously installed App.
   auto unsynced_system_web_app_manager =
-      std::make_unique<web_app::TestSystemWebAppManager>(profile());
+      std::make_unique<TestSystemWebAppManager>(profile());
 
   unsynced_system_web_app_manager->SetSubsystems(
       &externally_managed_app_manager(), &controller().registrar(),
diff --git a/chrome/browser/ash/system_web_apps/test_support/BUILD.gn b/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
index 6e75e7e..d1399fb7 100644
--- a/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
+++ b/chrome/browser/ash/system_web_apps/test_support/BUILD.gn
@@ -16,6 +16,8 @@
   sources = [
     "test_system_web_app_installation.cc",
     "test_system_web_app_installation.h",
+    "test_system_web_app_manager.cc",
+    "test_system_web_app_manager.h",
   ]
 
   deps = [
diff --git a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
index 4d44f1e..185694a 100644
--- a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
@@ -301,10 +301,9 @@
           base::Unretained(this)));
 
   test_system_web_app_manager_creator_ =
-      std::make_unique<web_app::TestSystemWebAppManagerCreator>(
-          base::BindRepeating(
-              &TestSystemWebAppInstallation::CreateSystemWebAppManager,
-              base::Unretained(this), delegate_ptr));
+      std::make_unique<TestSystemWebAppManagerCreator>(base::BindRepeating(
+          &TestSystemWebAppInstallation::CreateSystemWebAppManager,
+          base::Unretained(this), delegate_ptr));
 }
 
 TestSystemWebAppInstallation::TestSystemWebAppInstallation() {
@@ -319,7 +318,7 @@
           base::Unretained(this)));
 
   test_system_web_app_manager_creator_ =
-      std::make_unique<web_app::TestSystemWebAppManagerCreator>(
+      std::make_unique<TestSystemWebAppManagerCreator>(
           base::BindRepeating(&TestSystemWebAppInstallation::
                                   CreateSystemWebAppManagerWithNoSystemWebApps,
                               base::Unretained(this)));
diff --git a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h
index a935387..4dcb609 100644
--- a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.h
@@ -9,8 +9,8 @@
 
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_web_ui_controller_factory.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
@@ -249,7 +249,7 @@
 
   std::unique_ptr<web_app::FakeWebAppProviderCreator>
       fake_web_app_provider_creator_;
-  std::unique_ptr<web_app::TestSystemWebAppManagerCreator>
+  std::unique_ptr<TestSystemWebAppManagerCreator>
       test_system_web_app_manager_creator_;
 
   // nullopt if SetUpWithoutApps() was used.
diff --git a/chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.cc b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.cc
similarity index 84%
rename from chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.cc
rename to chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.cc
index f8cd939..6e37bda 100644
--- a/chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_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/web_applications/system_web_apps/test/test_system_web_app_manager.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 
 #include <memory>
 #include <string>
@@ -15,14 +15,15 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 
-namespace web_app {
+namespace ash {
 
 // static
 std::unique_ptr<KeyedService> TestSystemWebAppManager::BuildDefault(
     content::BrowserContext* context) {
   Profile* profile = Profile::FromBrowserContext(context);
 
-  WebAppProvider* provider = WebAppProvider::GetForLocalAppsUnchecked(profile);
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForLocalAppsUnchecked(profile);
   DCHECK(provider);
 
   auto test_swa_manager = std::make_unique<TestSystemWebAppManager>(profile);
@@ -46,8 +47,8 @@
 TestSystemWebAppManager::TestSystemWebAppManager(Profile* profile)
     : SystemWebAppManager(profile) {
   SetSystemAppsForTesting(
-      base::flat_map<ash::SystemWebAppType,
-                     std::unique_ptr<ash::SystemWebAppDelegate>>());
+      base::flat_map<SystemWebAppType,
+                     std::unique_ptr<SystemWebAppDelegate>>());
 }
 
 TestSystemWebAppManager::~TestSystemWebAppManager() = default;
@@ -80,7 +81,7 @@
 
 void TestSystemWebAppManagerCreator::OnWillCreateBrowserContextServices(
     content::BrowserContext* context) {
-  ash::SystemWebAppManagerFactory::GetInstance()->SetTestingFactory(
+  SystemWebAppManagerFactory::GetInstance()->SetTestingFactory(
       context, base::BindRepeating(
                    &TestSystemWebAppManagerCreator::CreateSystemWebAppManager,
                    base::Unretained(this)));
@@ -90,10 +91,10 @@
 TestSystemWebAppManagerCreator::CreateSystemWebAppManager(
     content::BrowserContext* context) {
   Profile* profile = Profile::FromBrowserContext(context);
-  DCHECK(!ash::SystemWebAppManagerFactory::IsServiceCreatedForProfile(profile));
-  if (!AreWebAppsEnabled(profile) || !callback_)
+  DCHECK(!SystemWebAppManagerFactory::IsServiceCreatedForProfile(profile));
+  if (!web_app::AreWebAppsEnabled(profile) || !callback_)
     return nullptr;
   return callback_.Run(profile);
 }
 
-}  // namespace web_app
+}  // namespace ash
diff --git a/chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h
similarity index 85%
rename from chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h
rename to chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h
index da31832..5b0c6a0 100644
--- a/chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_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_WEB_APPLICATIONS_SYSTEM_WEB_APPS_TEST_TEST_SYSTEM_WEB_APP_MANAGER_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_SYSTEM_WEB_APPS_TEST_TEST_SYSTEM_WEB_APP_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_TEST_SUPPORT_TEST_SYSTEM_WEB_APP_MANAGER_H_
+#define CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_TEST_SUPPORT_TEST_SYSTEM_WEB_APP_MANAGER_H_
 
 #include <string>
 #include <vector>
@@ -21,11 +21,11 @@
 class BrowserContext;
 }
 
-namespace web_app {
+namespace ash {
 
 // This class is used in unit tests only, browser tests use real
 // `SystemWebAppManager`.
-class TestSystemWebAppManager : public ash::SystemWebAppManager {
+class TestSystemWebAppManager : public SystemWebAppManager {
  public:
   // Used by the TestingProfile in unit tests.
   // Builds a stub `TestSystemWebAppManager` that needs to be manually started
@@ -53,7 +53,7 @@
     current_locale_ = locale;
   }
 
-  // ash::SystemWebAppManager:
+  // SystemWebAppManager:
   const base::Version& CurrentVersion() const override;
   const std::string& CurrentLocale() const override;
 
@@ -84,6 +84,6 @@
   base::CallbackListSubscription create_services_subscription_;
 };
 
-}  // namespace web_app
+}  // namespace ash
 
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_SYSTEM_WEB_APPS_TEST_TEST_SYSTEM_WEB_APP_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_SYSTEM_WEB_APPS_TEST_SUPPORT_TEST_SYSTEM_WEB_APP_MANAGER_H_
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
index 6fba258..6169010 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
@@ -51,8 +51,8 @@
       HandleBasePrompt(spec.base_prompt());
       break;
     case GenericPasswordChangeSpecification::SpecificationCase::
-        kGeneratedPasswordPrompt:
-      HandleGeneratedPasswordPrompt(spec.generated_password_prompt());
+        kUseGeneratedPasswordPrompt:
+      HandleGeneratedPasswordPrompt(spec.use_generated_password_prompt());
       break;
     case GenericPasswordChangeSpecification::SpecificationCase::
         kUpdateSidePanel:
@@ -170,16 +170,16 @@
   EndAction(true, std::move(serialized_result));
 }
 
-void ApcExternalActionDelegate::ShowGeneratedPasswordPrompt(
+void ApcExternalActionDelegate::ShowUseGeneratedPasswordPrompt(
     const autofill_assistant::password_change::
-        GeneratedPasswordPromptSpecification& password_prompt,
+        UseGeneratedPasswordPromptSpecification& password_prompt,
     const std::u16string& generated_password) {
   // Showing the prompt will override both the title and the description. Since
   // they cannot be reconstructed from the model due to the additional field
   // for the password, we clear the model.
   model_.title = std::u16string();
   model_.description = std::u16string();
-  password_change_run_display_->ShowGeneratedPasswordPrompt(
+  password_change_run_display_->ShowUseGeneratedPasswordPrompt(
       base::UTF8ToUTF16(password_prompt.title()), generated_password,
       base::UTF8ToUTF16(password_prompt.description()),
       PasswordChangeRunDisplay::PromptChoice{
@@ -194,11 +194,22 @@
               password_prompt.generated_password_choice().highlighted()});
 }
 
-void ApcExternalActionDelegate::OnGeneratedPasswordSelected(bool selected) {
+void ApcExternalActionDelegate::OnGeneratedPasswordSelected(
+    bool generated_password_accepted) {
   password_change_run_display_->ClearPrompt();
   SetTitle(std::u16string());
 
-  // TODO(crbug.com/1331202): Terminate action and send return value.
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification::
+      Result result;
+  result.set_generated_password_accepted(generated_password_accepted);
+
+  std::string serialized_result;
+  if (!result.SerializeToString(&serialized_result)) {
+    DLOG(ERROR) << "unable to base prompt result";
+    EndAction(false);
+    return;
+  }
+  EndAction(true, std::move(serialized_result));
 }
 
 void ApcExternalActionDelegate::ShowStartingScreen(const GURL& url) {
@@ -244,8 +255,10 @@
 
 void ApcExternalActionDelegate::HandleGeneratedPasswordPrompt(
     const autofill_assistant::password_change::
-        GeneratedPasswordPromptSpecification& specification) {
-  EndAction(false);
+        UseGeneratedPasswordPromptSpecification& specification) {
+  // TODO(crbug.com/1331202): Replace this hardcoded password with the real
+  // generated one.
+  ShowUseGeneratedPasswordPrompt(specification, u"verySecretPassword123");
 }
 
 void ApcExternalActionDelegate::HandleUpdateSidePanel(
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
index fdb48b7..e181ec1 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
@@ -57,9 +57,9 @@
       const autofill_assistant::password_change::BasePromptSpecification&
           base_prompt) override;
   void OnBasePromptChoiceSelected(size_t choice_index) override;
-  void ShowGeneratedPasswordPrompt(
+  void ShowUseGeneratedPasswordPrompt(
       const autofill_assistant::password_change::
-          GeneratedPasswordPromptSpecification& password_prompt,
+          UseGeneratedPasswordPromptSpecification& password_prompt,
       const std::u16string& generated_password) override;
   void OnGeneratedPasswordSelected(bool selected) override;
   void ShowStartingScreen(const GURL& url) override;
@@ -83,7 +83,7 @@
           specification);
   void HandleGeneratedPasswordPrompt(
       const autofill_assistant::password_change::
-          GeneratedPasswordPromptSpecification& specification);
+          UseGeneratedPasswordPromptSpecification& specification);
   void HandleUpdateSidePanel(
       const autofill_assistant::password_change::UpdateSidePanelSpecification&
           specification);
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc
index 50b6758..4c02237 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate_unittest.cc
@@ -69,9 +69,9 @@
 }
 
 // Helper function to create a sample proto for a generated password prompt.
-autofill_assistant::password_change::GeneratedPasswordPromptSpecification
-CreateGeneratedPasswordPrompt() {
-  autofill_assistant::password_change::GeneratedPasswordPromptSpecification
+autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification
+CreateUseGeneratedPasswordPrompt() {
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification
       proto;
 
   proto.set_title(kTitle);
@@ -79,18 +79,16 @@
 
   auto* choice = proto.mutable_manual_password_choice();
   choice->set_text(kPromptText1);
-  choice->set_highlighted(kIsHighlighted1);
+  choice->set_highlighted(false);
 
   choice = proto.mutable_generated_password_choice();
   choice->set_text(kPromptText2);
-  choice->set_highlighted(kIsHighlighted2);
+  choice->set_highlighted(true);
 
   return proto;
 }
 
 // Helper function that creates an `Action` from a `BasePromptSpecification`.
-// This function can be overloaded to handle all `password_change`
-// specifications.
 autofill_assistant::external::Action CreateAction(
     const autofill_assistant::password_change::BasePromptSpecification& proto) {
   autofill_assistant::external::Action action;
@@ -101,6 +99,19 @@
   return action;
 }
 
+// Helper function that creates an `Action` from a
+// `UseGeneratedPasswordPromptSpecification`.
+autofill_assistant::external::Action CreateAction(
+    const autofill_assistant::password_change::
+        UseGeneratedPasswordPromptSpecification& proto) {
+  autofill_assistant::external::Action action;
+  autofill_assistant::password_change::GenericPasswordChangeSpecification spec;
+  spec.mutable_use_generated_password_prompt()->CopyFrom(proto);
+  spec.SerializeToString(action.mutable_info()->mutable_action_payload());
+
+  return action;
+}
+
 }  // namespace
 
 class ApcExternalActionDelegateTest : public ::testing::Test {
@@ -165,29 +176,6 @@
   action_delegate()->OnInterruptFinished();
 }
 
-TEST_F(ApcExternalActionDelegateTest, ShowGeneratedPasswordPromptAndAccept) {
-  PasswordChangeRunDisplay::PromptChoice manual_choice, generated_choice;
-  EXPECT_CALL(*display(),
-              ShowGeneratedPasswordPrompt(
-                  base::UTF8ToUTF16(base::StringPiece(kTitle)),
-                  std::u16string(kPassword),
-                  base::UTF8ToUTF16(base::StringPiece(kDescription)), _, _))
-      .WillOnce(
-          DoAll(SaveArg<3>(&manual_choice), SaveArg<4>(&generated_choice)));
-  autofill_assistant::password_change::GeneratedPasswordPromptSpecification
-      proto = CreateGeneratedPasswordPrompt();
-  action_delegate()->ShowGeneratedPasswordPrompt(proto, kPassword);
-  EXPECT_EQ(manual_choice.text,
-            base::UTF8ToUTF16(base::StringPiece(kPromptText1)));
-  EXPECT_EQ(manual_choice.highlighted, kIsHighlighted1);
-  EXPECT_EQ(generated_choice.text,
-            base::UTF8ToUTF16(base::StringPiece(kPromptText2)));
-  EXPECT_EQ(generated_choice.highlighted, kIsHighlighted2);
-
-  EXPECT_CALL(*display(), ClearPrompt);
-  action_delegate()->OnGeneratedPasswordSelected(true);
-}
-
 TEST_F(ApcExternalActionDelegateTest, ShowStartingScreen) {
   const GURL url(kUrl);
 
@@ -320,3 +308,112 @@
   EXPECT_TRUE(result.success());
   EXPECT_FALSE(result.has_result_info());
 }
+
+TEST_F(ApcExternalActionDelegateTest,
+       ReceiveUseGeneratedPasswordPromptAction_GeneratedPasswordAccepted) {
+  base::MockOnceCallback<void(
+      const autofill_assistant::external::Result& result)>
+      result_callback;
+  base::MockOnceCallback<void(DomUpdateCallback)> start_dom_checks_callback;
+
+  // Save prompt arguments for inspection.
+  PasswordChangeRunDisplay::PromptChoice manual_choice, generated_choice;
+  EXPECT_CALL(*display(),
+              ShowUseGeneratedPasswordPrompt(
+                  base::UTF8ToUTF16(base::StringPiece(kTitle)),
+                  std::u16string(kPassword),
+                  base::UTF8ToUTF16(base::StringPiece(kDescription)), _, _))
+      .WillOnce(
+          DoAll(SaveArg<3>(&manual_choice), SaveArg<4>(&generated_choice)));
+
+  // Similarly, save the prompt result.
+  autofill_assistant::external::Result result;
+  EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result));
+
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification
+      proto = CreateUseGeneratedPasswordPrompt();
+  action_delegate()->OnActionRequested(CreateAction(proto),
+                                       start_dom_checks_callback.Get(),
+                                       result_callback.Get());
+
+  EXPECT_EQ(manual_choice.text,
+            base::UTF8ToUTF16(base::StringPiece(kPromptText1)));
+  EXPECT_EQ(manual_choice.highlighted, false);
+  EXPECT_EQ(generated_choice.text,
+            base::UTF8ToUTF16(base::StringPiece(kPromptText2)));
+  EXPECT_EQ(generated_choice.highlighted, true);
+
+  // But no result is sent yet.
+  EXPECT_FALSE(result.has_success());
+
+  // After simulating a click ...
+  EXPECT_CALL(*display(), ClearPrompt);
+  action_delegate()->OnGeneratedPasswordSelected(true);
+
+  // ... check success.
+  EXPECT_TRUE(result.has_success());
+  EXPECT_TRUE(result.success());
+  EXPECT_TRUE(result.has_result_info());
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification::
+      Result use_generated_password_prompt_specification_result;
+  EXPECT_TRUE(
+      use_generated_password_prompt_specification_result.ParseFromString(
+          result.result_info().result_payload()));
+  EXPECT_TRUE(use_generated_password_prompt_specification_result
+                  .generated_password_accepted());
+}
+
+TEST_F(ApcExternalActionDelegateTest,
+       ReceiveUseGeneratedPasswordPromptAction_ManualChoiceSelected) {
+  base::MockOnceCallback<void(
+      const autofill_assistant::external::Result& result)>
+      result_callback;
+  base::MockOnceCallback<void(DomUpdateCallback)> start_dom_checks_callback;
+
+  // Save prompt arguments for inspection.
+  PasswordChangeRunDisplay::PromptChoice manual_choice, generated_choice;
+  EXPECT_CALL(*display(),
+              ShowUseGeneratedPasswordPrompt(
+                  base::UTF8ToUTF16(base::StringPiece(kTitle)),
+                  std::u16string(kPassword),
+                  base::UTF8ToUTF16(base::StringPiece(kDescription)), _, _))
+      .WillOnce(
+          DoAll(SaveArg<3>(&manual_choice), SaveArg<4>(&generated_choice)));
+
+  // Similarly, save the prompt result.
+  autofill_assistant::external::Result result;
+  EXPECT_CALL(result_callback, Run).WillOnce(SaveArg<0>(&result));
+
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification
+      proto = CreateUseGeneratedPasswordPrompt();
+  // Remove the output key.
+  action_delegate()->OnActionRequested(CreateAction(proto),
+                                       start_dom_checks_callback.Get(),
+                                       result_callback.Get());
+
+  EXPECT_EQ(manual_choice.text,
+            base::UTF8ToUTF16(base::StringPiece(kPromptText1)));
+  EXPECT_EQ(manual_choice.highlighted, false);
+  EXPECT_EQ(generated_choice.text,
+            base::UTF8ToUTF16(base::StringPiece(kPromptText2)));
+  EXPECT_EQ(generated_choice.highlighted, true);
+
+  // But no result is sent yet.
+  EXPECT_FALSE(result.has_success());
+
+  // After simulating a click ...
+  EXPECT_CALL(*display(), ClearPrompt);
+  action_delegate()->OnGeneratedPasswordSelected(false);
+
+  // ... check success.
+  EXPECT_TRUE(result.has_success());
+  EXPECT_TRUE(result.success());
+  EXPECT_TRUE(result.has_result_info());
+  autofill_assistant::password_change::UseGeneratedPasswordPromptSpecification::
+      Result use_generated_password_prompt_specification_result;
+  EXPECT_TRUE(
+      use_generated_password_prompt_specification_result.ParseFromString(
+          result.result_info().result_payload()));
+  EXPECT_FALSE(use_generated_password_prompt_specification_result
+                   .generated_password_accepted());
+}
diff --git a/chrome/browser/autofill_assistant/password_change/proto/extensions.proto b/chrome/browser/autofill_assistant/password_change/proto/extensions.proto
index 53d63ea..66e39eef 100644
--- a/chrome/browser/autofill_assistant/password_change/proto/extensions.proto
+++ b/chrome/browser/autofill_assistant/password_change/proto/extensions.proto
@@ -71,7 +71,7 @@
 
 // Prompts the user to accept a generated password and shows that password
 // to the user.
-message GeneratedPasswordPromptSpecification {
+message UseGeneratedPasswordPromptSpecification {
   // Reserved value for the return key.
   reserved 1;
 
@@ -114,7 +114,7 @@
 message GenericPasswordChangeSpecification {
   oneof specification {
     BasePromptSpecification base_prompt = 1;
-    GeneratedPasswordPromptSpecification generated_password_prompt = 2;
+    UseGeneratedPasswordPromptSpecification use_generated_password_prompt = 2;
     UpdateSidePanelSpecification update_side_panel = 3;
   }
 }
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 0d4c13e..9cd8e9f 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -628,7 +628,7 @@
   // Tell the metrics service it was cleanly shutdown.
   metrics::MetricsService* metrics = g_browser_process->metrics_service();
   if (metrics) {
-    metrics->RecordStartOfSessionEnd();
+    metrics->LogCleanShutdown();
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
     // The MetricsService may update Local State prefs in memory without
     // writing the updated prefs to disk, so schedule a Local State write now.
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 6ecb5f99..ea81fece 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -77,9 +77,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/webui/scanning/url_constants.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/policy/networking/policy_cert_service.h"
 #include "chrome/browser/policy/networking/policy_cert_service_factory.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "components/user_manager/scoped_user_manager.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -125,7 +125,7 @@
     return swa_manager;
   }
   // The custom manager creator should be constructed before `TestingProfile`.
-  web_app::TestSystemWebAppManagerCreator test_system_web_app_manager_creator_;
+  ash::TestSystemWebAppManagerCreator test_system_web_app_manager_creator_;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index 9b0575a..43fe9fd 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -768,3 +768,54 @@
   EXPECT_TRUE(RunExtensionTest("file_browser/dlp_block", {},
                                {.load_as_component = true}));
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpMetadata) {
+  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
+      browser()->profile(),
+      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
+                          base::Unretained(this)));
+  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
+
+  AddLocalFileSystem(browser()->profile(), temp_dir_.GetPath());
+
+  const base::FilePath blocked_file_path =
+      temp_dir_.GetPath().Append("blocked_file.txt");
+  const base::FilePath unrestricted_file_path =
+      temp_dir_.GetPath().Append("unrestricted_file.txt");
+  const base::FilePath untracked_file_path =
+      temp_dir_.GetPath().Append("untracked_file.txt");
+
+  {
+    base::ScopedAllowBlockingForTesting allow_io;
+    base::File blocked_test_file(
+        blocked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(blocked_test_file.IsValid());
+
+    base::File unrestricted_test_file(
+        unrestricted_file_path,
+        base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(unrestricted_test_file.IsValid());
+
+    base::File untracked_test_file(
+        untracked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
+    ASSERT_TRUE(untracked_test_file.IsValid());
+  }
+
+  base::MockCallback<chromeos::DlpClient::AddFileCallback> add_file_cb;
+  EXPECT_CALL(add_file_cb, Run).Times(2);
+  dlp::AddFileRequest request;
+  request.set_file_path(blocked_file_path.value());
+  request.set_source_url("https://example1.com");
+  chromeos::DlpClient::Get()->AddFile(request, add_file_cb.Get());
+  request.set_file_path(unrestricted_file_path.value());
+  request.set_source_url("https://example2.com");
+  chromeos::DlpClient::Get()->AddFile(request, add_file_cb.Get());
+
+  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule)
+      .WillOnce(testing::Return(policy::DlpRulesManager::Level::kBlock))
+      .WillOnce(testing::Return(policy::DlpRulesManager::Level::kAllow))
+      .RetiresOnSaturation();
+
+  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata", {},
+                               {.load_as_component = true}));
+}
diff --git a/chrome/browser/chromeos/extensions/login_screen/BUILD.gn b/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
index c5b6c9f2..f2fc0a8 100644
--- a/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
@@ -73,7 +73,7 @@
   }
 }
 
-# TODO(jityao, b/215502706): Move tests back into chrome/test/BUILD.gn.
+# TODO(b/215502706): Move tests back into chrome/test/BUILD.gn.
 source_set("browser_tests") {
   testonly = true
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/browser/chromeos/extensions/login_screen/OWNERS b/chrome/browser/chromeos/extensions/login_screen/OWNERS
index fc0c3ac..aef9c2c 100644
--- a/chrome/browser/chromeos/extensions/login_screen/OWNERS
+++ b/chrome/browser/chromeos/extensions/login_screen/OWNERS
@@ -1,2 +1 @@
 hendrich@chromium.org
-jityao@google.com
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h
index ffab3dd..10ed4a9 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h
@@ -13,7 +13,6 @@
 // A cleanup handler which clears the profile's browsing data using
 // `BrowsingDataRemover`. See `chrome_browsing_data_remover::ALL_DATA_TYPES`
 // for the list of data types removed.
-// TODO(jityao, b:200678974) Add browser tests.
 class BrowsingDataCleanupHandler
     : public CleanupHandler,
       public content::BrowsingDataRemover::Observer {
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h
index 054a290..157ba422 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h
@@ -10,7 +10,6 @@
 namespace chromeos {
 
 // A cleanup handler which clears the profile's clipboard.
-// TODO(jityao, b:200678974) Add browser tests.
 class ClipboardCleanupHandler : public CleanupHandler {
  public:
   ClipboardCleanupHandler();
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h
index ce6011b0..8eae8c8 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h
@@ -16,7 +16,6 @@
 
 // A cleanup handler which clears the profile's My Files and Downloads
 // directories.
-// TODO(jityao, b:200678974) Add browser tests.
 class FilesCleanupHandler : public CleanupHandler {
  public:
   FilesCleanupHandler();
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h
index 59c8ae2..1ab2881 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h
@@ -16,7 +16,6 @@
 // A cleanup handler which closes all open browser windows. Windows created by
 // the chrome.app.window are not closed but they will be handled separately by
 // the extension and app cleanup handler.
-// TODO(jityao, b:200678974) Add browser tests.
 class OpenWindowsCleanupHandler : public CleanupHandler {
  public:
   OpenWindowsCleanupHandler();
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h
index bd22dc7..a3c02561 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h
@@ -10,7 +10,7 @@
 namespace chromeos {
 
 // A cleanup handler which clears the profile's print jobs.
-// TODO(jityao, b:200678974) Add browser tests.
+// TODO(b:200678974) Add Tast test.
 class PrintJobsCleanupHandler : public CleanupHandler {
  public:
   PrintJobsCleanupHandler();
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
index 114380d..f72236a 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
@@ -341,7 +341,7 @@
 
 ExtensionFunction::ResponseAction LoginEndSharedSessionFunction::Run() {
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // TODO(jityao, b:217155485): Enable for Lacros after cleanup handlers are
+  // TODO(b:217155485): Enable for Lacros after cleanup handlers are
   // added.
   return RespondNow(Error(kCannotBeCalledFromLacros));
 #else
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index 395c87c..9823435 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -435,6 +435,25 @@
         OnCompositionBoundsChanged::kEventName, std::move(args));
   }
 
+  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override {
+    if (extension_id_.empty() ||
+        !HasListener(input_method_private::OnCaretBoundsChanged::kEventName)) {
+      return;
+    }
+
+    // Note: this is a private API event;
+    input_method_private::OnCaretBoundsChanged::CaretBounds caret_bounds_arg;
+    caret_bounds_arg.x = caret_bounds.x();
+    caret_bounds_arg.y = caret_bounds.y();
+    caret_bounds_arg.w = caret_bounds.width();
+    caret_bounds_arg.h = caret_bounds.height();
+
+    DispatchEventToExtension(
+      extensions::events::INPUT_METHOD_PRIVATE_ON_CARET_BOUNDS_CHANGED,
+      input_method_private::OnCaretBoundsChanged::kEventName,
+      input_method_private::OnCaretBoundsChanged::Create(caret_bounds_arg));
+  }
+
   void OnFocus(
       const std::string& engine_id,
       int context_id,
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc
index a46c51f..65b38a1 100644
--- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc
+++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc
@@ -104,16 +104,19 @@
     updates_[id] = std::move(args);
   }
 
-  void StartUpdateCheck(
-      ExtensionDownloader* downloader,
-      ExtensionDownloaderDelegate* delegate,
-      std::unique_ptr<ManifestFetchData> fetch_data) override {
+  void StartUpdateCheck(ExtensionDownloader* downloader,
+                        ExtensionDownloaderDelegate* delegate,
+                        std::vector<ExtensionDownloaderTask> tasks) override {
+    std::set<int> request_ids;
+    for (const ExtensionDownloaderTask& task : tasks)
+      request_ids.insert(task.request_id);
     // Instead of immediately firing callbacks to the delegate in matching
     // cases below, we instead post a task since the delegate typically isn't
     // expecting a synchronous reply (the real code has to go do at least one
     // network request before getting a response, so this is is a reasonable
     // expectation by delegates).
-    for (const std::string& id : fetch_data->GetExtensionIds()) {
+    for (const ExtensionDownloaderTask& task : tasks) {
+      const ExtensionId& id = task.id;
       auto no_update = no_updates_.find(id);
       if (no_update != no_updates_.end()) {
         no_updates_.erase(no_update);
@@ -123,8 +126,7 @@
                 &ExtensionDownloaderDelegate::OnExtensionDownloadFailed,
                 base::Unretained(delegate), id,
                 ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE,
-                ExtensionDownloaderDelegate::PingResult(),
-                fetch_data->request_ids(),
+                ExtensionDownloaderDelegate::PingResult(), request_ids,
                 ExtensionDownloaderDelegate::FailureData()));
         continue;
       }
@@ -140,8 +142,7 @@
                 &ExtensionDownloaderDelegate::OnExtensionDownloadFinished,
                 base::Unretained(delegate), crx_info,
                 false /* file_ownership_passed */, GURL(),
-                ExtensionDownloaderDelegate::PingResult(),
-                fetch_data->request_ids(),
+                ExtensionDownloaderDelegate::PingResult(), request_ids,
                 ExtensionDownloaderDelegate::InstallCallback()));
         continue;
       }
diff --git a/chrome/browser/extensions/content_verifier_test_utils.cc b/chrome/browser/extensions/content_verifier_test_utils.cc
index b8755eb6..1768805 100644
--- a/chrome/browser/extensions/content_verifier_test_utils.cc
+++ b/chrome/browser/extensions/content_verifier_test_utils.cc
@@ -32,18 +32,22 @@
       std::make_pair(base::Version(version_string), crx_path);
 }
 
-const std::vector<std::unique_ptr<ManifestFetchData>>&
-DownloaderTestDelegate::requests() {
+const std::vector<ExtensionDownloaderTask>& DownloaderTestDelegate::requests() {
   return requests_;
 }
 
 void DownloaderTestDelegate::StartUpdateCheck(
     ExtensionDownloader* downloader,
     ExtensionDownloaderDelegate* delegate,
-    std::unique_ptr<ManifestFetchData> fetch_data) {
-  requests_.push_back(std::move(fetch_data));
-  const ManifestFetchData* data = requests_.back().get();
-  const ExtensionIdSet extension_ids = data->GetExtensionIds();
+    std::vector<ExtensionDownloaderTask> tasks) {
+  ExtensionIdSet extension_ids;
+  std::set<int> request_ids;
+  for (ExtensionDownloaderTask& task : tasks) {
+    extension_ids.insert(task.id);
+    request_ids.insert(task.request_id);
+  }
+  for (ExtensionDownloaderTask& task : tasks)
+    requests_.push_back(std::move(task));
   for (const auto& id : extension_ids) {
     if (base::Contains(responses_, id)) {
       CRXFileInfo crx_info(responses_[id].second, GetTestVerifierFormat());
@@ -59,7 +63,7 @@
               &ExtensionDownloaderDelegate::OnExtensionDownloadFinished,
               base::Unretained(delegate), crx_info,
               false /* pass_file_ownership */, GURL(),
-              ExtensionDownloaderDelegate::PingResult(), data->request_ids(),
+              ExtensionDownloaderDelegate::PingResult(), request_ids,
               ExtensionDownloaderDelegate::InstallCallback()));
     }
   }
diff --git a/chrome/browser/extensions/content_verifier_test_utils.h b/chrome/browser/extensions/content_verifier_test_utils.h
index 7c52e059..44266b06 100644
--- a/chrome/browser/extensions/content_verifier_test_utils.h
+++ b/chrome/browser/extensions/content_verifier_test_utils.h
@@ -27,7 +27,6 @@
 
 class ExtensionDownloader;
 class ExtensionDownloaderDelegate;
-class ManifestFetchData;
 
 namespace content_verifier_test {
 
@@ -49,16 +48,16 @@
                    const std::string& version_string,
                    const base::FilePath& crx_path);
 
-  const std::vector<std::unique_ptr<ManifestFetchData>>& requests();
+  const std::vector<ExtensionDownloaderTask>& requests();
 
   // ExtensionDownloaderTestDelegate:
   void StartUpdateCheck(ExtensionDownloader* downloader,
                         ExtensionDownloaderDelegate* delegate,
-                        std::unique_ptr<ManifestFetchData> fetch_data) override;
+                        std::vector<ExtensionDownloaderTask> tasks) override;
 
  private:
   // The requests we've received.
-  std::vector<std::unique_ptr<ManifestFetchData>> requests_;
+  std::vector<ExtensionDownloaderTask> requests_;
 
   // The prepared responses - this maps an extension id to a (version string,
   // crx file path) pair.
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl.h b/chrome/browser/fast_checkout/fast_checkout_client_impl.h
index 060b6015..f3769700 100644
--- a/chrome/browser/fast_checkout/fast_checkout_client_impl.h
+++ b/chrome/browser/fast_checkout/fast_checkout_client_impl.h
@@ -27,15 +27,16 @@
   void Stop() override;
   bool IsRunning() const override;
 
- private:
-  friend class content::WebContentsUserData<FastCheckoutClientImpl>;
-
+ protected:
   explicit FastCheckoutClientImpl(content::WebContents* web_contents);
 
   // Creates the external action deglegate and script controller.
-  std::unique_ptr<autofill_assistant::HeadlessScriptController>
+  virtual std::unique_ptr<autofill_assistant::HeadlessScriptController>
   CreateHeadlessScriptController();
 
+ private:
+  friend class content::WebContentsUserData<FastCheckoutClientImpl>;
+
   // Registers when a run is complete. Used in callbacks.
   void OnRunComplete(
       autofill_assistant::HeadlessScriptController::ScriptResult result);
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc b/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc
new file mode 100644
index 0000000..7a16d84
--- /dev/null
+++ b/chrome/browser/fast_checkout/fast_checkout_client_impl_unittest.cc
@@ -0,0 +1,150 @@
+// 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 "chrome/browser/fast_checkout/fast_checkout_client_impl.h"
+
+#include "base/test/gmock_move_support.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/fast_checkout/fast_checkout_features.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill_assistant/browser/public/mock_headless_script_controller.h"
+
+namespace {
+constexpr char kUrl[] = "https://www.example.com";
+}
+
+class TestFastCheckoutClientImpl : public FastCheckoutClientImpl {
+ public:
+  static TestFastCheckoutClientImpl* CreateForWebContents(
+      content::WebContents* web_contents);
+
+  explicit TestFastCheckoutClientImpl(content::WebContents* web_contents)
+      : FastCheckoutClientImpl(web_contents) {}
+
+  std::unique_ptr<autofill_assistant::HeadlessScriptController>
+  CreateHeadlessScriptController() override {
+    return std::move(external_script_controller_);
+  }
+
+  void InjectHeadlessScriptControllerForTesting(
+      std::unique_ptr<autofill_assistant::HeadlessScriptController>
+          external_script_controller) {
+    external_script_controller_ = std::move(external_script_controller);
+  }
+
+ private:
+  std::unique_ptr<autofill_assistant::HeadlessScriptController>
+      external_script_controller_;
+};
+
+// static
+TestFastCheckoutClientImpl* TestFastCheckoutClientImpl::CreateForWebContents(
+    content::WebContents* web_contents) {
+  const void* key = WebContentsUserData<FastCheckoutClientImpl>::UserDataKey();
+  web_contents->SetUserData(
+      key, std::make_unique<TestFastCheckoutClientImpl>(web_contents));
+  return static_cast<TestFastCheckoutClientImpl*>(
+      web_contents->GetUserData(key));
+}
+
+class FastCheckoutClientImplTest : public ChromeRenderViewHostTestHarness {
+ public:
+  FastCheckoutClientImplTest() {
+    feature_list_.InitWithFeatures({features::kFastCheckout}, {});
+  }
+
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+
+    test_client_ =
+        TestFastCheckoutClientImpl::CreateForWebContents(web_contents());
+
+    // Prepare the HeadlessScriptController.
+    auto external_script_controller =
+        std::make_unique<autofill_assistant::MockHeadlessScriptController>();
+    external_script_controller_ = external_script_controller.get();
+    test_client_->InjectHeadlessScriptControllerForTesting(
+        std::move(external_script_controller));
+  }
+
+  TestFastCheckoutClientImpl* fast_checkout_client() { return test_client_; }
+
+  autofill_assistant::MockHeadlessScriptController*
+  external_script_controller() {
+    return external_script_controller_;
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+
+  raw_ptr<TestFastCheckoutClientImpl> test_client_;
+  raw_ptr<autofill_assistant::MockHeadlessScriptController>
+      external_script_controller_;
+};
+
+TEST_F(
+    FastCheckoutClientImplTest,
+    GetOrCreateForWebContents_ClientWasAlreadyCreated_ReturnsExistingInstance) {
+  raw_ptr<FastCheckoutClient> client =
+      FastCheckoutClient::GetOrCreateForWebContents(web_contents());
+
+  // There is only one client per `WebContents`.
+  EXPECT_EQ(client, fast_checkout_client());
+}
+
+TEST_F(FastCheckoutClientImplTest, Start_FeatureEnabled_RunsSuccessfully) {
+  // `FastCheckoutClient` is not running initially.
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+
+  // Prepare to extract the callback to the external script controller.
+  base::OnceCallback<void(
+      autofill_assistant::HeadlessScriptController::ScriptResult)>
+      external_script_controller_callback;
+  EXPECT_CALL(*external_script_controller(), StartScript)
+      .Times(1)
+      .WillOnce(MoveArg<1>(&external_script_controller_callback));
+
+  // Starting the run successfully.
+  EXPECT_TRUE(fast_checkout_client()->Start(GURL(kUrl)));
+
+  // `FastCheckoutClient` is running.
+  EXPECT_TRUE(fast_checkout_client()->IsRunning());
+
+  // Cannot start another run.
+  EXPECT_FALSE(fast_checkout_client()->Start(GURL(kUrl)));
+
+  // Successful run.
+  autofill_assistant::HeadlessScriptController::ScriptResult script_result = {
+      /* success= */ true};
+  std::move(external_script_controller_callback).Run(script_result);
+
+  // `FastCheckoutClient` state was reset after run finished.
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+}
+
+TEST_F(FastCheckoutClientImplTest, Stop_WhenIsRunning_CancelsTheRun) {
+  // `FastCheckoutClient` is not running initially.
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+
+  // Starting the run successfully.
+  EXPECT_TRUE(fast_checkout_client()->Start(GURL(kUrl)));
+
+  fast_checkout_client()->Stop();
+
+  // `FastCheckoutClient` is not running anymore.
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+}
+
+TEST_F(FastCheckoutClientImplTest, Start_FeatureDisabled_NoRuns) {
+  // Disable Fast Checkout feature
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({}, {features::kFastCheckout});
+
+  // `FastCheckoutClient` is not running initially.
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+
+  // Starting is not successful which is also represented by the internal state.
+  EXPECT_FALSE(fast_checkout_client()->Start(GURL(kUrl)));
+  EXPECT_FALSE(fast_checkout_client()->IsRunning());
+}
diff --git a/chrome/browser/lifetime/browser_shutdown.cc b/chrome/browser/lifetime/browser_shutdown.cc
index e5f9e550..c76dc00 100644
--- a/chrome/browser/lifetime/browser_shutdown.cc
+++ b/chrome/browser/lifetime/browser_shutdown.cc
@@ -180,15 +180,15 @@
   // WARNING: During logoff/shutdown (WM_ENDSESSION) we may not have enough
   // time to get here. If you have something that *must* happen on end session,
   // consider putting it in BrowserProcessImpl::EndSession.
-  PrefService* prefs = g_browser_process->local_state();
-
   metrics::MetricsService* metrics = g_browser_process->metrics_service();
-  if (metrics)
-    metrics->RecordCompletedSessionEnd();
+  if (metrics) {
+    // TODO(crbug/1338797): LogCleanShutdown() is called earlier on in
+    // shutdown. See whether this call can be removed.
+    metrics->LogCleanShutdown();
+  }
 
   bool restart_last_session = RecordShutdownInfoPrefs();
-
-  prefs->CommitPendingWrite();
+  g_browser_process->local_state()->CommitPendingWrite();
 
 #if BUILDFLAG(ENABLE_RLZ)
   // Cleanup any statics created by RLZ. Must be done before NotificationService
diff --git a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
index fb59b08..9ea7409 100644
--- a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
+++ b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.cc
@@ -62,7 +62,7 @@
   }
 }
 
-bool HasChosenToSyncPreferences(syncer::SyncService* sync_service) {
+bool HasChosenToSyncPreferences(const syncer::SyncService* sync_service) {
   return sync_service && sync_service->IsSyncFeatureEnabled() &&
          sync_service->GetUserSettings()->GetSelectedTypes().Has(
              syncer::UserSelectableType::kPreferences);
diff --git a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
index c8b1976..f8151a0 100644
--- a/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
+++ b/chrome/browser/password_manager/android/password_manager_settings_service_android_impl.h
@@ -91,7 +91,7 @@
 
   // Sync service used to check whether the user has chosen to sync passwords
   // or settings.
-  raw_ptr<syncer::SyncService> sync_service_ = nullptr;
+  raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
   // Bridge used by the service to talk to the Java side.
   std::unique_ptr<password_manager::PasswordSettingsUpdaterAndroidBridge>
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.h b/chrome/browser/password_manager/android/password_store_android_backend.h
index 3ab1db87d..6b64558f 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend.h
@@ -253,7 +253,7 @@
   // This object is the proxy to the JNI bridge that performs the API requests.
   std::unique_ptr<PasswordStoreAndroidBackendBridge> bridge_;
 
-  raw_ptr<syncer::SyncService> sync_service_ = nullptr;
+  raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
   // Delegate to obtain sync status, and syncing account.
   std::unique_ptr<SyncDelegate> sync_delegate_;
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc
index 6321bd2d..010ba4ebb 100644
--- a/chrome/browser/policy/profile_policy_connector.cc
+++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -32,6 +32,10 @@
 #include "components/policy/core/common/schema_registry_tracking_policy_provider.h"
 #include "components/policy/policy_constants.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/policy/restricted_mgs_policy_provider.h"
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/policy/active_directory/active_directory_policy_manager.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -228,6 +232,16 @@
   }
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // `RestrictedMGSPolicyProvider::Create()` returns a nullptr when we are not
+  // in a Managed Guest Session.
+  restricted_mgs_policy_provider = RestrictedMGSPolicyProvider::Create();
+  if (restricted_mgs_policy_provider) {
+    restricted_mgs_policy_provider->Init(schema_registry);
+    policy_providers_.push_back(restricted_mgs_policy_provider.get());
+  }
+#endif
+
   std::vector<std::unique_ptr<PolicyMigrator>> migrators;
 #if BUILDFLAG(IS_WIN)
   migrators.push_back(
@@ -287,6 +301,11 @@
     special_user_policy_provider_->Shutdown();
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS)
+  if (restricted_mgs_policy_provider)
+    restricted_mgs_policy_provider->Shutdown();
+#endif
+
   for (auto& wrapped_policy_provider : wrapped_policy_providers_) {
     wrapped_policy_provider->Shutdown();
   }
diff --git a/chrome/browser/policy/profile_policy_connector.h b/chrome/browser/policy/profile_policy_connector.h
index 71bf1c8..28cd4ba 100644
--- a/chrome/browser/policy/profile_policy_connector.h
+++ b/chrome/browser/policy/profile_policy_connector.h
@@ -154,6 +154,10 @@
       nullptr;
   raw_ptr<const CloudPolicyStore> policy_store_ = nullptr;
 
+#if BUILDFLAG(IS_CHROMEOS)
+  std::unique_ptr<ConfigurationPolicyProvider> restricted_mgs_policy_provider;
+#endif
+
   // |policy_providers_| contains a list of the policy providers available for
   // the PolicyService of this connector, in decreasing order of priority.
   //
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider.cc b/chrome/browser/policy/restricted_mgs_policy_provider.cc
new file mode 100644
index 0000000..6dd6425e
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 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 "chrome/browser/policy/restricted_mgs_policy_provider.h"
+
+#include <utility>
+
+#include "base/values.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/settings/cros_settings.h"
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "chromeos/crosapi/mojom/device_settings_service.mojom.h"
+#include "chromeos/startup/browser_init_params.h"
+#endif
+
+namespace {
+
+bool IsDeviceRestrictedManagedGuestSessionEnabled() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  bool device_restricted_managed_guest_session_enabled = false;
+  ash::CrosSettings::Get()->GetBoolean(
+      ash::kDeviceRestrictedManagedGuestSessionEnabled,
+      &device_restricted_managed_guest_session_enabled);
+  return device_restricted_managed_guest_session_enabled;
+#else
+  const crosapi::mojom::BrowserInitParams* init_params =
+      chromeos::BrowserInitParams::Get();
+  if (!init_params)
+    return false;
+  return init_params->device_settings
+             ->device_restricted_managed_guest_session_enabled ==
+         crosapi::mojom::DeviceSettings::OptionalBool::kTrue;
+#endif
+}
+
+}  // namespace
+
+namespace policy {
+
+RestrictedMGSPolicyProvider::RestrictedMGSPolicyProvider() {
+  UpdatePolicyBundle();
+}
+
+RestrictedMGSPolicyProvider::~RestrictedMGSPolicyProvider() = default;
+
+// static
+std::unique_ptr<RestrictedMGSPolicyProvider>
+RestrictedMGSPolicyProvider::Create() {
+  if (!profiles::IsPublicSession())
+    return nullptr;
+  std::unique_ptr<RestrictedMGSPolicyProvider> provider(
+      new RestrictedMGSPolicyProvider());
+  return provider;
+}
+
+void RestrictedMGSPolicyProvider::RefreshPolicies() {}
+
+void RestrictedMGSPolicyProvider::UpdatePolicyBundle() {
+  std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
+  weak_factory_.InvalidateWeakPtrs();
+  bundle->CopyFrom(policies());
+
+  PolicyMap& chrome_policy =
+      bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  if (IsDeviceRestrictedManagedGuestSessionEnabled())
+    ApplyRestrictedManagedGuestSessionOverride(&chrome_policy);
+
+  UpdatePolicy(std::move(bundle));
+}
+
+// Details about the restricted managed guest session and the overridden
+// policies can be found here: go/restricted-managed-guest-session.
+void RestrictedMGSPolicyProvider::ApplyRestrictedManagedGuestSessionOverride(
+    PolicyMap* chrome_policy) {
+  std::pair<std::string, base::Value> policy_overrides[] = {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {key::kArcEnabled, base::Value(false)},
+    {key::kCrostiniAllowed, base::Value(false)},
+    {key::kDeletePrintJobHistoryAllowed, base::Value(true)},
+    {key::kKerberosEnabled, base::Value(false)},
+    {key::kNetworkFileSharesAllowed, base::Value(false)},
+    {key::kUserBorealisAllowed, base::Value(false)},
+    {key::kUserPluginVmAllowed, base::Value(false)},
+#endif
+    {key::kAllowDeletingBrowserHistory, base::Value(true)},
+    {key::kCACertificateManagementAllowed,
+     base::Value(static_cast<int>(CACertificateManagementPermission::kNone))},
+    {key::kClientCertificateManagementAllowed,
+     base::Value(
+         static_cast<int>(ClientCertificateManagementPermission::kNone))},
+    {key::kEnableMediaRouter, base::Value(false)},
+    {key::kPasswordManagerEnabled, base::Value(false)},
+    {key::kScreenCaptureAllowed, base::Value(false)},
+  };
+
+  for (auto& policy_override : policy_overrides) {
+    chrome_policy->Set(policy_override.first, POLICY_LEVEL_MANDATORY,
+                       POLICY_SCOPE_USER,
+                       POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                       std::move(policy_override.second), nullptr);
+  }
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider.h b/chrome/browser/policy/restricted_mgs_policy_provider.h
new file mode 100644
index 0000000..9e08b735
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef CHROME_BROWSER_POLICY_RESTRICTED_MGS_POLICY_PROVIDER_H_
+#define CHROME_BROWSER_POLICY_RESTRICTED_MGS_POLICY_PROVIDER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/policy/core/common/configuration_policy_provider.h"
+
+namespace policy {
+
+// Policy provider for a restricted Managed Guest Session (MGS). The provider
+// applies restrictions on a list of policies if the
+// DeviceRestrictedManagedGuestSessionEnabled policy is enabled when the session
+// starts. The restricted MGS is only used in the context of shared sessions. If
+// shared sessions are enabled, the session will not launch without the enabled
+// DeviceRestrictedManagedGuestSessionEnabled policy. We do not check for
+// updates of the policy in session because if it is disabled during a running
+// shared session, it will deny the unlock of the shared session.
+class RestrictedMGSPolicyProvider : public ConfigurationPolicyProvider {
+ public:
+  RestrictedMGSPolicyProvider();
+
+  RestrictedMGSPolicyProvider(const RestrictedMGSPolicyProvider&) = delete;
+  RestrictedMGSPolicyProvider& operator=(const RestrictedMGSPolicyProvider&) =
+      delete;
+
+  ~RestrictedMGSPolicyProvider() override;
+
+  // ConfigurationPolicyProvider:
+  void RefreshPolicies() override;
+
+  // Factory function to create and initialize a provider. Returns nullptr if we
+  // are not in a Managed Guest Session.
+  static std::unique_ptr<RestrictedMGSPolicyProvider> Create();
+
+ private:
+  // Gets the current PolicyBundle and applies restrictions in case the
+  // DeviceRestrictedManagedGuestSessionEnabled policy is enabled.
+  void UpdatePolicyBundle();
+
+  void ApplyRestrictedManagedGuestSessionOverride(PolicyMap* chrome_policy);
+
+  base::WeakPtrFactory<RestrictedMGSPolicyProvider> weak_factory_{this};
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_RESTRICTED_MGS_POLICY_PROVIDER_H_
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider_ash_browsertest.cc b/chrome/browser/policy/restricted_mgs_policy_provider_ash_browsertest.cc
new file mode 100644
index 0000000..20fe846
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider_ash_browsertest.cc
@@ -0,0 +1,180 @@
+// 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 "ash/constants/ash_switches.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h"
+#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
+#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/policy/proto/chrome_device_policy.pb.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+const char kEmail[] = "user@test";
+
+const PolicyNamespace kChromeNamespace(POLICY_DOMAIN_CHROME, std::string());
+
+void AddRestrictedPoliciesToMap(PolicyMap* policy_map) {
+  policy_map->Set(key::kArcEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kCrostiniAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kDeletePrintJobHistoryAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(true), nullptr);
+  policy_map->Set(key::kKerberosEnabled, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kNetworkFileSharesAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kUserBorealisAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kUserPluginVmAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kAllowDeletingBrowserHistory, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(true), nullptr);
+  policy_map->Set(
+      key::kCACertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
+      POLICY_SCOPE_USER,
+      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+      base::Value(static_cast<int>(CACertificateManagementPermission::kNone)),
+      nullptr);
+  policy_map->Set(key::kClientCertificateManagementAllowed,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(static_cast<int>(
+                      ClientCertificateManagementPermission::kNone)),
+                  nullptr);
+  policy_map->Set(key::kEnableMediaRouter, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+  policy_map->Set(key::kScreenCaptureAllowed, POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                  base::Value(false), nullptr);
+}
+
+}  // namespace
+
+class RestrictedMGSPolicyProviderAshBrowserTest
+    : public DevicePolicyCrosBrowserTest {
+ public:
+  // DevicePolicyCrosBrowserTest:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(ash::switches::kLoginManager);
+    command_line->AppendSwitch(ash::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(ash::switches::kOobeSkipPostLogin);
+  }
+
+  void SetUpPolicy(bool restricted) {
+    em::DeviceLocalAccountsProto* const device_local_accounts =
+        device_policy()->payload().mutable_device_local_accounts();
+    em::DeviceLocalAccountInfoProto* const account =
+        device_local_accounts->add_account();
+    account->set_account_id(kEmail);
+    account->set_type(
+        em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
+    device_local_accounts->set_auto_login_id(kEmail);
+    device_local_accounts->set_auto_login_delay(0);
+    SetRestrictedPolicy(restricted);
+    // Save base policy map before the RestrictedMGSPolicyProvider is created.
+    SaveExpectedPolicyMap();
+    RefreshDevicePolicy();
+  }
+
+  void SetRestrictedPolicy(bool restricted) {
+    em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
+    proto.mutable_device_restricted_managed_guest_session_enabled()
+        ->set_enabled(restricted);
+    policy_helper()->RefreshPolicyAndWaitUntilDeviceSettingsUpdated(
+        {ash::kDeviceRestrictedManagedGuestSessionEnabled});
+  }
+
+  void SaveExpectedPolicyMap() {
+    expected_policy_map_ = GetCurrentChromePolicies();
+    SetEnterpriseUsersDefaults(&expected_policy_map_);
+
+    // Values implicitly enforced for public accounts.
+    expected_policy_map_.Set(key::kShelfAutoHideBehavior,
+                             POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                             POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                             base::Value("Never"), nullptr);
+    expected_policy_map_.Set(key::kShowLogoutButtonInTray,
+                             POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                             POLICY_SOURCE_ENTERPRISE_DEFAULT,
+                             base::Value(true), nullptr);
+  }
+
+  PolicyMap GetCurrentChromePolicies() {
+    auto* profile = ProfileManager::GetPrimaryUserProfile();
+    auto* policy_connector = profile->GetProfilePolicyConnector();
+    return policy_connector->policy_service()
+        ->GetPolicies(kChromeNamespace)
+        .Clone();
+  }
+
+ protected:
+  PolicyMap expected_policy_map_;
+  ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
+};
+
+IN_PROC_BROWSER_TEST_F(RestrictedMGSPolicyProviderAshBrowserTest,
+                       DeviceRestrictedManagedGuestSessionDisabled) {
+  SetUpPolicy(/*restricted=*/false);
+  ash::SessionStateWaiter(session_manager::SessionState::ACTIVE).Wait();
+
+  auto current_policy_map = GetCurrentChromePolicies();
+
+  // Policy map stays unchanged.
+  EXPECT_TRUE(expected_policy_map_.Equals(current_policy_map));
+}
+
+IN_PROC_BROWSER_TEST_F(RestrictedMGSPolicyProviderAshBrowserTest,
+                       DeviceRestrictedManagedGuestSessionEnabled) {
+  SetUpPolicy(/*restricted=*/true);
+  ash::SessionStateWaiter(session_manager::SessionState::ACTIVE).Wait();
+
+  auto current_policy_map = GetCurrentChromePolicies();
+
+  // Policy map has the restricted policies.
+  AddRestrictedPoliciesToMap(&expected_policy_map_);
+  EXPECT_TRUE(expected_policy_map_.Equals(current_policy_map));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider_ash_unittest.cc b/chrome/browser/policy/restricted_mgs_policy_provider_ash_unittest.cc
new file mode 100644
index 0000000..8bc876f
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider_ash_unittest.cc
@@ -0,0 +1,167 @@
+// 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 "chrome/browser/policy/restricted_mgs_policy_provider.h"
+
+#include <memory>
+
+#include "ash/components/settings/cros_settings_names.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/ash/settings/device_settings_test_helper.h"
+#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+std::unique_ptr<PolicyBundle> BuildRestrictedPolicyBundle() {
+  auto policy_bundle = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map =
+      policy_bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  policy_map.Set(key::kArcEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kCrostiniAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kDeletePrintJobHistoryAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(true), nullptr);
+  policy_map.Set(key::kKerberosEnabled, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kNetworkFileSharesAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kUserBorealisAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kUserPluginVmAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kAllowDeletingBrowserHistory, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(true), nullptr);
+  policy_map.Set(
+      key::kCACertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
+      POLICY_SCOPE_USER,
+      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+      base::Value(static_cast<int>(CACertificateManagementPermission::kNone)),
+      nullptr);
+  policy_map.Set(key::kClientCertificateManagementAllowed,
+                 POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(static_cast<int>(
+                     ClientCertificateManagementPermission::kNone)),
+                 nullptr);
+  policy_map.Set(key::kEnableMediaRouter, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kScreenCaptureAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+
+  return policy_bundle;
+}
+
+}  // namespace
+
+class RestrictedMGSPolicyProviderAshTest : public ash::DeviceSettingsTestBase {
+ public:
+  void SetUp() override {
+    ash::DeviceSettingsTestBase::SetUp();
+    chromeos::LoginState::Initialize();
+    cros_settings_ = std::make_unique<ash::CrosSettings>(
+        device_settings_service_.get(),
+        TestingBrowserProcess::GetGlobal()->local_state());
+
+    cros_settings_helper_ = std::make_unique<ash::ScopedCrosSettingsTestHelper>(
+        /*create_service=*/false);
+    cros_settings_helper_->ReplaceDeviceSettingsProviderWithStub();
+  }
+
+  void TearDown() override {
+    cros_settings_.reset();
+    cros_settings_helper_.reset();
+    ash::DeviceSettingsTestBase::TearDown();
+    chromeos::LoginState::Shutdown();
+  }
+
+  std::unique_ptr<ash::CrosSettings> cros_settings_;
+  std::unique_ptr<ash::ScopedCrosSettingsTestHelper> cros_settings_helper_;
+};
+
+TEST_F(RestrictedMGSPolicyProviderAshTest, CreateRestrictedMGSPolicyProvider) {
+  // Doesn't get created for a regular user.
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_REGULAR);
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  EXPECT_FALSE(policy_provider);
+
+  // Gets created for a Managed Guest Session.
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  policy_provider = RestrictedMGSPolicyProvider::Create();
+  EXPECT_TRUE(policy_provider);
+}
+
+TEST_F(RestrictedMGSPolicyProviderAshTest,
+       DeviceRestrictedManagedGuestSessionDisabled) {
+  cros_settings_helper_->SetBoolean(
+      ash::kDeviceRestrictedManagedGuestSessionEnabled, false);
+  // Empty policy bundle.
+  PolicyMap expected_policy_map;
+  PolicyBundle expected_policy_bundle;
+  expected_policy_bundle.Get(PolicyNamespace(
+      POLICY_DOMAIN_CHROME, std::string())) = expected_policy_map.Clone();
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  ASSERT_TRUE(policy_provider);
+  EXPECT_TRUE(expected_policy_bundle.Equals(policy_provider->policies()));
+}
+
+TEST_F(RestrictedMGSPolicyProviderAshTest,
+       DeviceRestrictedManagedGuestSessionEnabled) {
+  cros_settings_helper_->SetBoolean(
+      ash::kDeviceRestrictedManagedGuestSessionEnabled, true);
+  auto expected_policy_bundle = BuildRestrictedPolicyBundle();
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  ASSERT_TRUE(policy_provider);
+  EXPECT_TRUE(expected_policy_bundle->Equals(policy_provider->policies()));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider_lacros_browsertest.cc b/chrome/browser/policy/restricted_mgs_policy_provider_lacros_browsertest.cc
new file mode 100644
index 0000000..e12f5a3
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider_lacros_browsertest.cc
@@ -0,0 +1,108 @@
+// 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 "base/values.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/startup/browser_init_params.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_service.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace policy {
+
+namespace {
+
+const PolicyNamespace kChromeNamespace(POLICY_DOMAIN_CHROME, std::string());
+
+PolicyMap BuildRestrictedPolicyMap() {
+  PolicyMap policy_map;
+  policy_map.Set(key::kAllowDeletingBrowserHistory, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(true), nullptr);
+  policy_map.Set(
+      key::kCACertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
+      POLICY_SCOPE_USER,
+      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+      base::Value(static_cast<int>(CACertificateManagementPermission::kNone)),
+      nullptr);
+  policy_map.Set(key::kClientCertificateManagementAllowed,
+                 POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(static_cast<int>(
+                     ClientCertificateManagementPermission::kNone)),
+                 nullptr);
+  policy_map.Set(key::kEnableMediaRouter, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kScreenCaptureAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+
+  return policy_map;
+}
+
+}  // namespace
+
+class RestrictedMGSPolicyProviderLacrosBrowserTest
+    : public InProcessBrowserTest,
+      public testing::WithParamInterface<
+          crosapi::mojom::DeviceSettings::OptionalBool> {
+ public:
+  void SetUp() override {
+    // The value of DeviceRestrictedManagedGuestSessionEnabled is passed as a
+    // test parameter.
+    SetInitParams(
+        /*session_type=*/crosapi::mojom::SessionType::kPublicSession,
+        /*restricted=*/GetParam());
+    InProcessBrowserTest::SetUp();
+  }
+
+  void SetInitParams(crosapi::mojom::SessionType session_type,
+                     crosapi::mojom::DeviceSettings_OptionalBool restricted) {
+    auto params = crosapi::mojom::BrowserInitParams::New();
+    params = crosapi::mojom::BrowserInitParams::New();
+    params->session_type = session_type;
+    params->device_settings = crosapi::mojom::DeviceSettings::New();
+    params->device_settings->device_restricted_managed_guest_session_enabled =
+        restricted;
+    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(params));
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    RestrictedMGSPolicyProviderLacrosBrowserTest,
+    testing::Values(crosapi::mojom::DeviceSettings::OptionalBool::kFalse,
+                    crosapi::mojom::DeviceSettings::OptionalBool::kTrue));
+
+IN_PROC_BROWSER_TEST_P(RestrictedMGSPolicyProviderLacrosBrowserTest,
+                       DeviceRestrictedManagedGuestSessionEnabled) {
+  auto* profile = ProfileManager::GetPrimaryUserProfile();
+  auto* policy_connector = profile->GetProfilePolicyConnector();
+  const PolicyMap& current_policy_map =
+      policy_connector->policy_service()->GetPolicies(kChromeNamespace);
+
+  PolicyMap expected_policy_map;
+  if (GetParam() == crosapi::mojom::DeviceSettings::OptionalBool::kTrue)
+    expected_policy_map = BuildRestrictedPolicyMap();
+
+  EXPECT_TRUE(expected_policy_map.Equals(current_policy_map));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/restricted_mgs_policy_provider_lacros_unittest.cc b/chrome/browser/policy/restricted_mgs_policy_provider_lacros_unittest.cc
new file mode 100644
index 0000000..bfff0e6
--- /dev/null
+++ b/chrome/browser/policy/restricted_mgs_policy_provider_lacros_unittest.cc
@@ -0,0 +1,124 @@
+// 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 "chrome/browser/policy/restricted_mgs_policy_provider.h"
+
+#include <memory>
+
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "chromeos/crosapi/mojom/device_settings_service.mojom.h"
+#include "chromeos/startup/browser_init_params.h"
+#include "components/policy/core/common/policy_bundle.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_namespace.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+std::unique_ptr<PolicyBundle> BuildRestrictedPolicyBundle() {
+  auto policy_bundle = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map =
+      policy_bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  policy_map.Set(key::kAllowDeletingBrowserHistory, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(true), nullptr);
+  policy_map.Set(
+      key::kCACertificateManagementAllowed, POLICY_LEVEL_MANDATORY,
+      POLICY_SCOPE_USER,
+      POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+      base::Value(static_cast<int>(CACertificateManagementPermission::kNone)),
+      nullptr);
+  policy_map.Set(key::kClientCertificateManagementAllowed,
+                 POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(static_cast<int>(
+                     ClientCertificateManagementPermission::kNone)),
+                 nullptr);
+  policy_map.Set(key::kEnableMediaRouter, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kPasswordManagerEnabled, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+  policy_map.Set(key::kScreenCaptureAllowed, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER,
+                 POLICY_SOURCE_RESTRICTED_MANAGED_GUEST_SESSION_OVERRIDE,
+                 base::Value(false), nullptr);
+
+  return policy_bundle;
+}
+
+}  // namespace
+
+class RestrictedMGSPolicyProviderLacrosTest : public testing::Test {
+ protected:
+  void SetInitParams(crosapi::mojom::SessionType session_type,
+                     crosapi::mojom::DeviceSettings_OptionalBool restricted) {
+    auto params = crosapi::mojom::BrowserInitParams::New();
+    params = crosapi::mojom::BrowserInitParams::New();
+    params->session_type = session_type;
+    params->device_settings = crosapi::mojom::DeviceSettings::New();
+    params->device_settings->device_restricted_managed_guest_session_enabled =
+        restricted;
+    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(params));
+  }
+};
+
+TEST_F(RestrictedMGSPolicyProviderLacrosTest,
+       CreateRestrictedMGSPolicyProvider) {
+  // Doesn't get created for a regular session.
+  SetInitParams(
+      /*session_type=*/crosapi::mojom::SessionType::kRegularSession,
+      /*restricted=*/crosapi::mojom::DeviceSettings::OptionalBool::kUnset);
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  EXPECT_FALSE(policy_provider);
+
+  // Gets created for a Managed Guest Session.
+  SetInitParams(
+      /*session_type=*/crosapi::mojom::SessionType::kPublicSession,
+      /*restricted=*/crosapi::mojom::DeviceSettings::OptionalBool::kTrue);
+  policy_provider = RestrictedMGSPolicyProvider::Create();
+  EXPECT_TRUE(policy_provider);
+}
+
+TEST_F(RestrictedMGSPolicyProviderLacrosTest,
+       DeviceRestrictedManagedGuestSessionDisabled) {
+  SetInitParams(
+      /*session_type=*/crosapi::mojom::SessionType::kPublicSession,
+      /*restricted=*/crosapi::mojom::DeviceSettings::OptionalBool::kFalse);
+  // Empty policy bundle.
+  PolicyMap expected_policy_map;
+  PolicyBundle expected_policy_bundle;
+  expected_policy_bundle.Get(PolicyNamespace(
+      POLICY_DOMAIN_CHROME, std::string())) = expected_policy_map.Clone();
+
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  ASSERT_TRUE(policy_provider);
+  EXPECT_TRUE(expected_policy_bundle.Equals(policy_provider->policies()));
+}
+
+TEST_F(RestrictedMGSPolicyProviderLacrosTest,
+       DeviceRestrictedManagedGuestSessionEnabled) {
+  SetInitParams(
+      /*session_type=*/crosapi::mojom::SessionType::kPublicSession,
+      /*restricted=*/crosapi::mojom::DeviceSettings::OptionalBool::kTrue);
+  auto expected_policy_bundle = BuildRestrictedPolicyBundle();
+
+  auto policy_provider = RestrictedMGSPolicyProvider::Create();
+  ASSERT_TRUE(policy_provider);
+  EXPECT_TRUE(expected_policy_bundle->Equals(policy_provider->policies()));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/resources/chromeos/login/screens/common/arc_terms_of_service.js b/chrome/browser/resources/chromeos/login/screens/common/arc_terms_of_service.js
index 6b8229b3..997d69f 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/arc_terms_of_service.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/arc_terms_of_service.js
@@ -18,6 +18,12 @@
 };
 
 /**
+ * Timeout to load online ToS.
+ * @type {number}
+ */
+const ONLINE_LOAD_TIMEOUT_IN_MS = 10000;
+
+/**
  * @constructor
  * @extends {PolymerElement}
  * @implements {LoginScreenBehaviorInterface}
@@ -336,10 +342,6 @@
     this.pageReady_ = true;
 
     var termsView = this.$.arcTosView;
-    var requestFilter = {urls: ['<all_urls>'], types: ['main_frame']};
-
-    termsView.request.onErrorOccurred.addListener(
-        this.onTermsViewErrorOccurred.bind(this), requestFilter);
 
     // Open links from webview in overlay dialog.
     var self = this;
@@ -579,8 +581,27 @@
     }
     this.termsError = false;
     this.usingOfflineTermsForTesting_ = false;
-    var termsView = this.$.arcTosView;
-    termsView.src = this.termsOfServiceHostName_ + '/about/play-terms.html';
+
+    const loadFailureCallback = () => {
+      // If in demo mode fallback to offline Terms of Service copy.
+      if (this.isDemoModeSetup_() && this.usingOfflineTermsForTesting_) {
+        const TERMS_URL = 'chrome://terms/arc/terms';
+        const webView = this.$.arcTosView;
+        WebViewHelper.loadUrlContentToWebView(
+            webView, TERMS_URL, WebViewHelper.ContentType.HTML);
+        return;
+      }
+      this.showError_();
+    };
+
+    const termsView = this.$.arcTosView;
+    const tosLoader = new WebViewLoader(
+        termsView, ONLINE_LOAD_TIMEOUT_IN_MS, loadFailureCallback,
+        this.isDemoModeSetup_() /* clear_anchors */, false /* inject_css */);
+
+    const tosUrl = this.termsOfServiceHostName_ + '/about/play-terms.html';
+    tosLoader.setUrl(tosUrl);
+
     this.setUIStep(ArcTosState.LOADING);
     this.enableButtons_(false);
   }
@@ -673,26 +694,6 @@
   }
 
   /**
-   * Handles event when terms view cannot be loaded.
-   */
-  onTermsViewErrorOccurred(details) {
-    // If in demo mode fallback to offline Terms of Service copy.
-    if (this.isDemoModeSetup_() && this.usingOfflineTermsForTesting_) {
-      const TERMS_URL = 'chrome://terms/arc/terms';
-      var webView = this.$.arcTosView;
-      WebViewHelper.loadUrlContentToWebView(
-          webView, TERMS_URL, WebViewHelper.ContentType.HTML);
-      return;
-    } else if (details && details.error == 'net::ERR_ABORTED') {
-      // Retry triggers net::ERR_ABORTED, so ignore it.
-      // TODO(b/232592745): Replace with a state machine to handle aborts
-      // gracefully and avoid duplicate reloads.
-      return;
-    }
-    this.showError_();
-  }
-
-  /**
    * Shows error UI when terms view cannot be loaded or terms content cannot
    * be fetched from webview.
    */
diff --git a/chrome/browser/resources/chromeos/login/screens/common/theme_selection.html b/chrome/browser/resources/chromeos/login/screens/common/theme_selection.html
index 770832a..a8f9032 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/theme_selection.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/theme_selection.html
@@ -64,8 +64,13 @@
       <h1 slot="title" id="theme-selection-title">
         [[i18nDynamic(locale, 'themeSelectionScreenTitle')]]
       </h1>
-      <div slot="subtitle" id="theme-selection-subtitle">
-        [[i18nDynamic(locale, 'themeSelectionScreenDescription')]]
+      <div hidden="[[isInTabletMode_]]" slot="subtitle"
+          id="theme-selection-subtitle-clamshell">
+        [[i18nDynamic(locale, 'themeSelectionScreenDescriptionClamshell')]]
+      </div>
+      <div hidden="[[!isInTabletMode_]]" slot="subtitle"
+          id="theme-selection-subtitle-tablet">
+        [[i18nDynamic(locale, 'themeSelectionScreenDescriptionTablet')]]
       </div>
       <div slot="content" class="layout vertical landscape-vertical-centered">
         <cr-radio-group id="theme" selected="{{selectedTheme}}">
diff --git a/chrome/browser/resources/chromeos/login/screens/common/theme_selection.js b/chrome/browser/resources/chromeos/login/screens/common/theme_selection.js
index 21bd601bc..03bdc94 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/theme_selection.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/theme_selection.js
@@ -54,6 +54,15 @@
        */
       selectedTheme: {
         type: String,
+      },
+
+      /**
+       * Indicates if the device is used in tablet mode
+       * @private
+       */
+      isInTabletMode_: {
+        type: Boolean,
+        value: false,
       }
     };
   }
@@ -66,6 +75,15 @@
     return [];
   }
 
+  /**
+   * Updates "device in tablet mode" state when tablet mode is changed.
+   * Overridden from LoginScreenBehavior.
+   * @param {boolean} isInTabletMode True when in tablet mode.
+   */
+  setTabletModeState(isInTabletMode) {
+    this.isInTabletMode_ = isInTabletMode;
+  }
+
   ready() {
     super.ready();
     this.initializeLoginScreen('ThemeSelectionScreen');
diff --git a/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting.svg b/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting.svg
index 1c3e3a4..af02cb9 100644
--- a/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting.svg
+++ b/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting.svg
@@ -1,16 +1 @@
-<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M192.841 132.878H164.379C163.813 132.858 163.276 132.624 162.875 132.224C162.475 131.823 162.241 131.286 162.221 130.72V69.1586C162.221 68.5861 162.448 68.0371 162.853 67.6323C163.258 67.2274 163.807 67 164.379 67H192.841C193.408 67.0199 193.945 67.2537 194.346 67.6543C194.746 68.0549 194.98 68.5925 195 69.1586V130.64C195 131.22 194.776 131.777 194.373 132.195C193.97 132.612 193.421 132.857 192.841 132.878Z" fill="#D2E3FC"/>
-<path d="M192.841 132.878H164.379C163.813 132.858 163.276 132.624 162.875 132.224C162.475 131.823 162.241 131.286 162.221 130.72V69.1586C162.221 68.5861 162.448 68.0371 162.853 67.6323C163.258 67.2274 163.807 67 164.379 67H192.841C193.408 67.0199 193.945 67.2537 194.346 67.6543C194.746 68.0549 194.98 68.5925 195 69.1586V130.64C195 131.22 194.776 131.777 194.373 132.195C193.97 132.612 193.421 132.857 192.841 132.878Z" fill="#D2E3FC"/>
-<path d="M82.4315 68.7887L63.0038 71.7468C60.4806 72.1285 58.2123 73.4969 56.6979 75.551C55.1836 77.6051 54.5473 80.1766 54.9289 82.6998C55.3106 85.2231 56.679 87.4914 58.7331 89.0057C60.7872 90.52 63.3587 91.1564 65.882 90.7747L85.3097 87.7366C86.6019 87.604 87.8537 87.2103 88.9892 86.5794C90.1247 85.9485 91.1203 85.0936 91.9155 84.0665C92.7108 83.0394 93.2892 81.8615 93.6157 80.6042C93.9422 79.3469 94.0099 78.0364 93.8149 76.7521C93.6198 75.4679 93.1659 74.2366 92.4808 73.1329C91.7957 72.0293 90.8936 71.0763 89.8292 70.3317C88.7648 69.5871 87.5603 69.0664 86.2887 68.8011C85.017 68.5359 83.7048 68.5316 82.4315 68.7887V68.7887ZM90.9861 77.1833C91.1335 78.1132 91.095 79.0631 90.8727 79.978C90.6504 80.8928 90.2487 81.7545 89.691 82.513C89.1333 83.2715 88.4306 83.9118 87.6236 84.3967C86.8166 84.8817 85.9214 85.2017 84.9899 85.3382L65.4823 88.3762C63.5951 88.6625 61.6715 88.1873 60.1347 87.0553C58.5979 85.9233 57.5737 84.2272 57.2875 82.3401C57.0012 80.4529 57.4763 78.5294 58.6083 76.9925C59.7403 75.4557 61.4365 74.4315 63.3236 74.1453L82.8312 71.1072C84.7182 70.8328 86.6369 71.3185 88.166 72.4578C89.695 73.5971 90.7093 75.2967 90.9861 77.1833V77.1833Z" fill="#EA4335"/>
-<path d="M111.853 75.9543C112.705 75.8262 113.482 75.3967 114.044 74.7438C114.607 74.0909 114.916 73.258 114.916 72.3965C114.916 71.535 114.607 70.7021 114.044 70.0493C113.482 69.3964 112.705 68.9669 111.853 68.8388C111.341 68.7618 110.818 68.7963 110.321 68.9397C109.824 69.0832 109.363 69.3323 108.971 69.67C108.578 70.0078 108.264 70.4262 108.048 70.8968C107.832 71.3673 107.72 71.8789 107.72 72.3965C107.72 72.9142 107.832 73.4258 108.048 73.8963C108.264 74.3669 108.578 74.7853 108.971 75.1231C109.363 75.4608 109.824 75.7099 110.321 75.8534C110.818 75.9968 111.341 76.0312 111.853 75.9543Z" fill="#34A853"/>
-<path d="M65.0825 80.5412L37.9797 87.8965C37.8584 87.9148 37.7448 87.9675 37.6526 88.0485C37.5604 88.1295 37.4935 88.2353 37.4598 88.3533C37.4261 88.4713 37.427 88.5965 37.4625 88.714C37.498 88.8314 37.5665 88.9362 37.6599 89.0158L57.6472 108.843C57.7366 108.913 57.8413 108.961 57.9527 108.983C58.0642 109.005 58.1792 109 58.2883 108.968C58.3975 108.937 58.4977 108.881 58.5808 108.803C58.6639 108.726 58.7276 108.63 58.7665 108.523L65.9619 81.3407C65.9781 81.2247 65.9658 81.1065 65.926 80.9964C65.8861 80.8863 65.82 80.7875 65.7333 80.7087C65.6467 80.6299 65.5421 80.5735 65.4287 80.5443C65.3153 80.5151 65.1964 80.5141 65.0825 80.5412V80.5412Z" fill="#FBBC04"/>
-<path d="M162.221 92.184C159.493 92.3074 156.825 91.3607 154.786 89.5457L152.067 87.1473L150.868 86.1879L149.909 85.6282C148.871 85.0934 147.738 84.7704 146.574 84.6782C145.411 84.5859 144.24 84.7262 143.132 85.0908C142.023 85.4554 140.998 86.0371 140.116 86.802C139.234 87.5668 138.514 88.4995 137.996 89.5457C136.959 91.6489 136.789 94.0748 137.522 96.3022C138.254 98.5296 139.831 100.381 141.914 101.458L142.953 101.858H143.033L144.472 102.258L147.99 102.977C150.703 103.591 153.078 105.222 154.626 107.534L156.385 110.173C156.871 111.028 157.404 111.856 157.983 112.651C159.165 113.993 160.606 115.081 162.221 115.849" fill="#D2E3FC"/>
-<path d="M162.221 115.929L163.1 116.329C166.132 117.459 169.481 117.391 172.465 116.138C175.448 114.885 177.842 112.541 179.157 109.585C180.472 106.628 180.61 103.281 179.543 100.226C178.476 97.171 176.284 94.6381 173.414 93.1434C170.953 91.9657 168.189 91.5749 165.499 92.0241L162.381 92.184H162.221" fill="#4285F4"/>
-<path d="M137.676 75.4746C135.083 75.4746 132.548 76.2436 130.392 77.6843C128.236 79.1251 126.555 81.1728 125.563 83.5687C124.57 85.9645 124.311 88.6008 124.817 91.1442C125.323 93.6877 126.571 96.0239 128.405 97.8576C130.239 99.6913 132.575 100.94 135.118 101.446C137.662 101.952 140.298 101.692 142.694 100.7C145.09 99.7075 147.138 98.0269 148.578 95.8707C150.019 93.7145 150.788 91.1795 150.788 88.5863C150.788 86.8644 150.449 85.1594 149.79 83.5687C149.131 81.9779 148.165 80.5325 146.948 79.3149C145.73 78.0974 144.285 77.1316 142.694 76.4727C141.103 75.8138 139.398 75.4746 137.676 75.4746V75.4746ZM137.676 77.8731C139.795 77.8731 141.867 78.5014 143.628 79.6786C145.39 80.8558 146.763 82.5289 147.574 84.4865C148.385 86.4441 148.597 88.5982 148.184 90.6763C147.77 92.7545 146.75 94.6634 145.252 96.1617C143.753 97.6599 141.845 98.6803 139.766 99.0936C137.688 99.507 135.534 99.2948 133.577 98.484C131.619 97.6731 129.946 96.3 128.769 94.5382C127.591 92.7764 126.963 90.7051 126.963 88.5863C126.963 85.745 128.092 83.02 130.101 81.0109C132.11 79.0018 134.835 77.8731 137.676 77.8731Z" fill="#4285F4"/>
-<path d="M182.608 70.198C182.699 70.1972 182.789 70.216 182.873 70.253C182.956 70.2901 183.03 70.3445 183.091 70.4126C183.151 70.4808 183.197 70.5611 183.224 70.6481C183.251 70.7352 183.259 70.8271 183.247 70.9175C183.249 71.0742 183.192 71.2258 183.089 71.3437C182.986 71.4616 182.843 71.5375 182.688 71.5571H176.372C176.208 71.5397 176.055 71.4668 175.939 71.3504C175.823 71.234 175.75 71.0812 175.732 70.9175C175.741 70.7548 175.799 70.5988 175.899 70.4702C175.999 70.3416 176.136 70.2466 176.292 70.198H182.608Z" fill="#4285F4"/>
-<path d="M172.934 71.717C173.146 71.717 173.349 71.6328 173.499 71.4828C173.649 71.3329 173.733 71.1295 173.733 70.9175C173.746 70.8094 173.734 70.6998 173.699 70.597C173.663 70.4941 173.605 70.4007 173.528 70.3237C173.451 70.2467 173.357 70.1883 173.255 70.1526C173.152 70.117 173.042 70.1052 172.934 70.118C172.722 70.118 172.519 70.2022 172.369 70.3522C172.219 70.5021 172.134 70.7055 172.134 70.9175C172.153 71.1235 172.243 71.3165 172.389 71.4628C172.535 71.609 172.728 71.699 172.934 71.717V71.717Z" fill="#4285F4"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M147.67 84.8287C148.454 84.9747 149.21 85.2446 149.909 85.6282L150.468 85.948C150.693 86.8091 150.8 87.6965 150.788 88.5863C150.786 91.4062 149.875 94.1503 148.19 96.4114C146.505 98.6726 144.136 100.33 141.434 101.138C140.559 100.633 139.776 99.9843 139.116 99.2195C141.68 98.855 144.028 97.5803 145.731 95.6281C147.433 93.676 148.377 91.1766 148.39 88.5863C148.39 87.2995 148.146 86.0243 147.67 84.8287V84.8287Z" fill="white"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M57.1675 82.6998C57.4874 84.3125 58.3385 85.7715 59.5848 86.8439C60.8311 87.9163 62.4007 88.5402 64.0431 88.616L63.4835 90.6147C61.5081 90.421 59.6455 89.6039 58.1651 88.2817C56.6847 86.9594 55.6634 85.2005 55.2487 83.2594L57.1675 82.6998Z" fill="white"/>
-<path d="M15.1142 98.9295C18.2762 97.8175 21.743 97.9508 24.8102 99.3025C27.8775 100.654 30.3152 103.123 31.6281 106.207C32.9411 109.291 33.0309 112.759 31.8793 115.906C30.7276 119.054 28.4209 121.646 25.4277 123.154C24.5942 123.627 23.7089 124.003 22.7894 124.273" fill="#EE5FFA"/>
-<path d="M8.95813 76.064L24.868 130.19L79.7132 129.47V132.588L23.6688 133.308C23.3502 133.307 23.0399 133.207 22.7816 133.02C22.5234 132.834 22.3303 132.571 22.2297 132.268L6 76.9435L8.95813 76.064Z" fill="#4285F4"/>
-</svg>
+<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M192.841 132.878h-28.462a2.242 2.242 0 0 1-2.158-2.158V69.159A2.157 2.157 0 0 1 164.379 67h28.462A2.24 2.24 0 0 1 195 69.159v61.481a2.239 2.239 0 0 1-2.159 2.238Z" fill="#D2E3FC"/><path d="M192.841 132.878h-28.462a2.242 2.242 0 0 1-2.158-2.158V69.159A2.157 2.157 0 0 1 164.379 67h28.462A2.24 2.24 0 0 1 195 69.159v61.481a2.239 2.239 0 0 1-2.159 2.238Z" fill="#D2E3FC"/><path d="m82.431 68.789-19.427 2.958a9.623 9.623 0 0 0 2.878 19.028l19.428-3.038a9.592 9.592 0 0 0 7.17-14.604 9.594 9.594 0 0 0-10.049-4.344Zm8.555 8.394a7.117 7.117 0 0 1-5.996 8.155l-19.508 3.038a7.197 7.197 0 1 1-2.158-14.23l19.507-3.039a7.196 7.196 0 0 1 8.155 6.076Z" fill="#EA4335"/><path d="M111.853 75.954a3.596 3.596 0 0 0 3.063-3.557 3.596 3.596 0 0 0-6.868-1.5 3.595 3.595 0 0 0 2.273 4.956 3.6 3.6 0 0 0 1.532.101Z" fill="#34A853"/><path d="M65.082 80.541 37.98 87.897a.64.64 0 0 0-.32 1.119l19.987 19.827a.723.723 0 0 0 .641.125.71.71 0 0 0 .479-.445l7.195-27.182a.72.72 0 0 0-.88-.8Z" fill="#FBBC04"/><path d="M162.221 92.184a10.47 10.47 0 0 1-7.435-2.638l-2.719-2.399-1.199-.96-.959-.559a8.796 8.796 0 0 0-11.913 3.918 8.95 8.95 0 0 0-.474 6.756 8.95 8.95 0 0 0 4.392 5.156l1.039.4h.08l1.439.4 3.518.719a10.876 10.876 0 0 1 6.636 4.557l1.759 2.639a26.206 26.206 0 0 0 1.598 2.478 13.203 13.203 0 0 0 4.238 3.198" fill="#D2E3FC"/><path d="m162.221 115.929.879.4a12.717 12.717 0 0 0 16.057-6.744 12.715 12.715 0 0 0-5.743-16.442 13.273 13.273 0 0 0-7.915-1.119l-3.118.16h-.16M137.676 75.475a13.11 13.11 0 1 0 0 26.22 13.11 13.11 0 0 0 0-26.22Zm0 2.398a10.711 10.711 0 0 1 10.508 12.803 10.72 10.72 0 0 1-8.418 8.418 10.71 10.71 0 0 1-10.997-4.556 10.707 10.707 0 0 1 1.332-13.527 10.713 10.713 0 0 1 7.575-3.138ZM182.608 70.198a.642.642 0 0 1 .616.45.63.63 0 0 1 .023.27.636.636 0 0 1-.559.64h-6.316a.718.718 0 0 1-.64-.64.8.8 0 0 1 .56-.72h6.316ZM172.934 71.717a.799.799 0 0 0 .799-.8.726.726 0 0 0-.205-.593.726.726 0 0 0-.594-.206.799.799 0 0 0-.8.8.878.878 0 0 0 .8.8v-.001Z" fill="#4285F4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M147.67 84.829c.784.146 1.54.416 2.239.8l.559.319c.225.861.332 1.749.32 2.638a13.111 13.111 0 0 1-9.354 12.552 9.012 9.012 0 0 1-2.318-1.919 10.795 10.795 0 0 0 9.274-10.633c0-1.287-.244-2.562-.72-3.757ZM57.167 82.7a7.355 7.355 0 0 0 6.876 5.916l-.56 1.999a9.355 9.355 0 0 1-8.234-7.356l1.918-.56Z" fill="#fff"/><path d="M15.114 98.93a13.191 13.191 0 0 1 10.314 24.224c-.834.473-1.72.849-2.639 1.119" fill="#EE5FFA"/><path d="m8.958 76.064 15.91 54.126 54.845-.72v3.118l-56.044.72a1.52 1.52 0 0 1-1.44-1.04L6 76.943l2.958-.879Z" fill="#4285F4"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting_dark.svg b/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting_dark.svg
index f345fb2..e07a4ba 100644
--- a/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting_dark.svg
+++ b/chrome/browser/resources/settings/chromeos/images/multidevice_permission_setup_connecting_dark.svg
@@ -1,16 +1 @@
-<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M192.841 132.878H164.379C163.813 132.858 163.276 132.624 162.875 132.224C162.475 131.823 162.241 131.286 162.221 130.72V69.1586C162.221 68.5861 162.448 68.0371 162.853 67.6323C163.258 67.2274 163.807 67 164.379 67H192.841C193.408 67.0199 193.945 67.2537 194.346 67.6543C194.746 68.0549 194.98 68.5925 195 69.1586V130.64C195 131.22 194.776 131.777 194.373 132.195C193.97 132.612 193.421 132.857 192.841 132.878Z" fill="#8AB4F8" fill-opacity="0.4"/>
-<path d="M192.841 132.878H164.379C163.813 132.858 163.276 132.624 162.875 132.224C162.475 131.823 162.241 131.286 162.221 130.72V69.1586C162.221 68.5861 162.448 68.0371 162.853 67.6323C163.258 67.2274 163.807 67 164.379 67H192.841C193.408 67.0199 193.945 67.2537 194.346 67.6543C194.746 68.0549 194.98 68.5925 195 69.1586V130.64C195 131.22 194.776 131.777 194.373 132.195C193.97 132.612 193.421 132.857 192.841 132.878Z" fill="#8AB4F8" fill-opacity="0.4"/>
-<path d="M82.4315 68.7887L63.0038 71.7468C60.4806 72.1285 58.2123 73.4969 56.6979 75.551C55.1836 77.6051 54.5473 80.1766 54.9289 82.6998C55.3106 85.2231 56.679 87.4914 58.7331 89.0057C60.7872 90.52 63.3587 91.1564 65.882 90.7747L85.3097 87.7366C86.6019 87.604 87.8537 87.2103 88.9892 86.5794C90.1247 85.9485 91.1203 85.0936 91.9155 84.0665C92.7108 83.0394 93.2892 81.8615 93.6157 80.6042C93.9422 79.3469 94.0099 78.0364 93.8149 76.7521C93.6198 75.4679 93.1659 74.2366 92.4808 73.1329C91.7957 72.0293 90.8936 71.0763 89.8292 70.3317C88.7648 69.5871 87.5603 69.0664 86.2887 68.8011C85.017 68.5359 83.7048 68.5316 82.4315 68.7887V68.7887ZM90.9861 77.1833C91.1335 78.1132 91.095 79.0631 90.8727 79.978C90.6504 80.8928 90.2487 81.7545 89.691 82.513C89.1333 83.2715 88.4306 83.9118 87.6236 84.3967C86.8166 84.8817 85.9214 85.2017 84.9899 85.3382L65.4823 88.3762C63.5951 88.6625 61.6715 88.1873 60.1347 87.0553C58.5979 85.9233 57.5737 84.2272 57.2875 82.3401C57.0012 80.4529 57.4763 78.5294 58.6083 76.9925C59.7403 75.4557 61.4365 74.4315 63.3236 74.1453L82.8312 71.1072C84.7182 70.8328 86.6369 71.3185 88.166 72.4578C89.695 73.5971 90.7093 75.2967 90.9861 77.1833V77.1833Z" fill="#EA4335"/>
-<path d="M111.853 75.9543C112.705 75.8262 113.482 75.3967 114.044 74.7438C114.607 74.0909 114.916 73.258 114.916 72.3965C114.916 71.535 114.607 70.7021 114.044 70.0493C113.482 69.3964 112.705 68.9669 111.853 68.8388C111.341 68.7618 110.818 68.7963 110.321 68.9397C109.824 69.0832 109.363 69.3323 108.971 69.67C108.578 70.0078 108.264 70.4262 108.048 70.8968C107.832 71.3673 107.72 71.8789 107.72 72.3965C107.72 72.9142 107.832 73.4258 108.048 73.8963C108.264 74.3669 108.578 74.7853 108.971 75.1231C109.363 75.4608 109.824 75.7099 110.321 75.8534C110.818 75.9968 111.341 76.0312 111.853 75.9543Z" fill="#5BB974"/>
-<path d="M65.0825 80.5412L37.9797 87.8965C37.8584 87.9148 37.7448 87.9675 37.6526 88.0485C37.5604 88.1295 37.4935 88.2353 37.4598 88.3533C37.4261 88.4713 37.427 88.5965 37.4625 88.714C37.498 88.8314 37.5665 88.9362 37.6599 89.0158L57.6472 108.843C57.7366 108.913 57.8413 108.961 57.9527 108.983C58.0642 109.005 58.1792 109 58.2883 108.968C58.3975 108.937 58.4977 108.881 58.5808 108.803C58.6639 108.726 58.7276 108.63 58.7665 108.523L65.9619 81.3407C65.9781 81.2247 65.9658 81.1065 65.926 80.9964C65.8861 80.8863 65.82 80.7875 65.7333 80.7087C65.6467 80.6299 65.5421 80.5735 65.4287 80.5443C65.3153 80.5151 65.1964 80.5141 65.0825 80.5412V80.5412Z" fill="#FBBC04"/>
-<path d="M162.221 92.184C159.493 92.3074 156.825 91.3607 154.786 89.5457L152.067 87.1473L150.868 86.1879L149.909 85.6282C148.871 85.0934 147.738 84.7704 146.574 84.6782C145.411 84.5859 144.24 84.7262 143.132 85.0908C142.023 85.4554 140.998 86.0371 140.116 86.802C139.234 87.5668 138.514 88.4995 137.996 89.5457C136.959 91.6489 136.789 94.0748 137.522 96.3022C138.254 98.5296 139.831 100.381 141.914 101.458L142.953 101.858H143.033L144.472 102.258L147.99 102.977C150.703 103.591 153.078 105.222 154.626 107.534L156.385 110.173C156.871 111.028 157.404 111.856 157.983 112.651C159.165 113.993 160.606 115.081 162.221 115.849" fill="#8AB4F8" fill-opacity="0.4"/>
-<path d="M162.221 115.929L163.1 116.329C166.132 117.459 169.481 117.391 172.465 116.138C175.448 114.885 177.842 112.541 179.157 109.585C180.472 106.628 180.61 103.281 179.543 100.226C178.476 97.171 176.284 94.6381 173.414 93.1434C170.953 91.9657 168.189 91.5749 165.499 92.0241L162.381 92.184H162.221" fill="#669DF6"/>
-<path d="M137.676 75.4746C135.083 75.4746 132.548 76.2436 130.392 77.6843C128.236 79.1251 126.555 81.1728 125.563 83.5687C124.57 85.9645 124.311 88.6008 124.817 91.1442C125.323 93.6877 126.571 96.0239 128.405 97.8576C130.239 99.6913 132.575 100.94 135.118 101.446C137.662 101.952 140.298 101.692 142.694 100.7C145.09 99.7075 147.138 98.0269 148.578 95.8707C150.019 93.7145 150.788 91.1795 150.788 88.5863C150.788 86.8644 150.449 85.1594 149.79 83.5687C149.131 81.9779 148.165 80.5325 146.948 79.3149C145.73 78.0974 144.285 77.1316 142.694 76.4727C141.103 75.8138 139.398 75.4746 137.676 75.4746V75.4746ZM137.676 77.8731C139.795 77.8731 141.867 78.5014 143.628 79.6786C145.39 80.8558 146.763 82.5289 147.574 84.4865C148.385 86.4441 148.597 88.5982 148.184 90.6763C147.77 92.7545 146.75 94.6634 145.252 96.1617C143.753 97.6599 141.845 98.6803 139.766 99.0936C137.688 99.507 135.534 99.2948 133.577 98.484C131.619 97.6731 129.946 96.3 128.769 94.5382C127.591 92.7764 126.963 90.7051 126.963 88.5863C126.963 85.745 128.092 83.02 130.101 81.0109C132.11 79.0018 134.835 77.8731 137.676 77.8731Z" fill="#669DF6"/>
-<path d="M182.608 70.1979C182.699 70.1972 182.789 70.216 182.873 70.253C182.956 70.29 183.03 70.3444 183.091 70.4126C183.151 70.4807 183.197 70.561 183.224 70.6481C183.251 70.7352 183.259 70.827 183.247 70.9174C183.249 71.0741 183.192 71.2257 183.089 71.3436C182.986 71.4615 182.843 71.5375 182.688 71.557H176.372C176.208 71.5397 176.055 71.4667 175.939 71.3503C175.823 71.2339 175.75 71.0811 175.732 70.9174C175.741 70.7548 175.799 70.5987 175.899 70.4701C175.999 70.3415 176.136 70.2465 176.292 70.1979H182.608Z" fill="#669DF6"/>
-<path d="M172.934 71.7169C173.146 71.7169 173.349 71.6327 173.499 71.4828C173.649 71.3328 173.733 71.1295 173.733 70.9174C173.746 70.8094 173.734 70.6998 173.699 70.5969C173.663 70.4941 173.605 70.4006 173.528 70.3237C173.451 70.2467 173.357 70.1882 173.255 70.1526C173.152 70.1169 173.042 70.1051 172.934 70.118C172.722 70.118 172.519 70.2022 172.369 70.3521C172.219 70.5021 172.134 70.7054 172.134 70.9174C172.153 71.1234 172.243 71.3165 172.389 71.4627C172.535 71.6089 172.728 71.6989 172.934 71.7169V71.7169Z" fill="#669DF6"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M147.67 84.8286C148.454 84.9747 149.21 85.2445 149.909 85.6281L150.468 85.9479C150.693 86.809 150.8 87.6964 150.788 88.5862C150.786 91.4061 149.875 94.1503 148.19 96.4114C146.505 98.6725 144.136 100.33 141.434 101.138C140.559 100.633 139.776 99.9842 139.116 99.2195C141.68 98.8549 144.028 97.5802 145.731 95.6281C147.433 93.6759 148.377 91.1765 148.39 88.5862C148.39 87.2994 148.146 86.0242 147.67 84.8286V84.8286Z" fill="#323336"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M57.1675 82.6998C57.4874 84.3126 58.3385 85.7716 59.5848 86.844C60.8311 87.9164 62.4007 88.5403 64.0431 88.6161L63.4835 90.6148C61.5081 90.4211 59.6455 89.604 58.1651 88.2817C56.6847 86.9595 55.6634 85.2006 55.2487 83.2595L57.1675 82.6998Z" fill="#323336"/>
-<path d="M15.1142 98.9295C18.2762 97.8175 21.743 97.9508 24.8102 99.3025C27.8775 100.654 30.3152 103.123 31.6281 106.207C32.9411 109.291 33.0309 112.759 31.8793 115.906C30.7276 119.054 28.4209 121.646 25.4277 123.154C24.5942 123.627 23.7089 124.003 22.7894 124.273" fill="#F882FE"/>
-<path d="M8.95813 76.064L24.868 130.19L79.7132 129.47V132.588L23.6688 133.308C23.3502 133.307 23.0399 133.207 22.7816 133.02C22.5234 132.834 22.3303 132.571 22.2297 132.268L6 76.9434L8.95813 76.064Z" fill="#669DF6"/>
-</svg>
+<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M192.841 132.878h-28.462a2.242 2.242 0 0 1-2.158-2.158V69.159A2.157 2.157 0 0 1 164.379 67h28.462A2.24 2.24 0 0 1 195 69.159v61.481a2.239 2.239 0 0 1-2.159 2.238Z" fill="#8AB4F8" fill-opacity=".4"/><path d="M192.841 132.878h-28.462a2.242 2.242 0 0 1-2.158-2.158V69.159A2.157 2.157 0 0 1 164.379 67h28.462A2.24 2.24 0 0 1 195 69.159v61.481a2.239 2.239 0 0 1-2.159 2.238Z" fill="#8AB4F8" fill-opacity=".4"/><path d="m82.431 68.789-19.427 2.958a9.623 9.623 0 0 0 2.878 19.028l19.428-3.038a9.592 9.592 0 0 0 7.17-14.604 9.594 9.594 0 0 0-10.049-4.344Zm8.555 8.394a7.117 7.117 0 0 1-5.996 8.155l-19.508 3.038a7.197 7.197 0 1 1-2.158-14.23l19.507-3.039a7.196 7.196 0 0 1 8.155 6.076Z" fill="#EA4335"/><path d="M111.853 75.954a3.596 3.596 0 0 0 3.063-3.557 3.596 3.596 0 0 0-6.868-1.5 3.595 3.595 0 0 0 2.273 4.956 3.6 3.6 0 0 0 1.532.101Z" fill="#5BB974"/><path d="M65.082 80.541 37.98 87.897a.64.64 0 0 0-.32 1.119l19.987 19.827a.723.723 0 0 0 .641.125.71.71 0 0 0 .479-.445l7.195-27.182a.72.72 0 0 0-.88-.8Z" fill="#FBBC04"/><path d="M162.221 92.184a10.47 10.47 0 0 1-7.435-2.638l-2.719-2.399-1.199-.96-.959-.559a8.796 8.796 0 0 0-11.913 3.918 8.95 8.95 0 0 0-.474 6.756 8.95 8.95 0 0 0 4.392 5.156l1.039.4h.08l1.439.4 3.518.719a10.876 10.876 0 0 1 6.636 4.557l1.759 2.639a26.206 26.206 0 0 0 1.598 2.478 13.203 13.203 0 0 0 4.238 3.198" fill="#8AB4F8" fill-opacity=".4"/><path d="m162.221 115.929.879.4a12.717 12.717 0 0 0 16.057-6.744 12.715 12.715 0 0 0-5.743-16.442 13.273 13.273 0 0 0-7.915-1.119l-3.118.16h-.16M137.676 75.475a13.11 13.11 0 1 0 0 26.22 13.11 13.11 0 0 0 0-26.22Zm0 2.398a10.711 10.711 0 0 1 10.508 12.803 10.72 10.72 0 0 1-8.418 8.418 10.71 10.71 0 0 1-10.997-4.556 10.707 10.707 0 0 1 1.332-13.527 10.713 10.713 0 0 1 7.575-3.138ZM182.608 70.198a.644.644 0 0 1 .616.45.63.63 0 0 1 .023.27.636.636 0 0 1-.559.639h-6.316a.718.718 0 0 1-.64-.64.8.8 0 0 1 .56-.72h6.316ZM172.934 71.717a.799.799 0 0 0 .799-.8.726.726 0 0 0-.205-.593.725.725 0 0 0-.594-.206.799.799 0 0 0-.8.8.878.878 0 0 0 .8.8v-.001Z" fill="#669DF6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M147.67 84.829c.784.146 1.54.416 2.239.8l.559.319c.225.861.332 1.748.32 2.638a13.111 13.111 0 0 1-9.354 12.552 9.015 9.015 0 0 1-2.318-1.919 10.796 10.796 0 0 0 9.274-10.633c0-1.287-.244-2.562-.72-3.757ZM57.167 82.7a7.355 7.355 0 0 0 6.876 5.916l-.56 1.999a9.354 9.354 0 0 1-8.234-7.355l1.918-.56Z" fill="#323336"/><path d="M15.114 98.93a13.191 13.191 0 0 1 10.314 24.224c-.834.473-1.72.849-2.639 1.119" fill="#F882FE"/><path d="m8.958 76.064 15.91 54.126 54.845-.72v3.118l-56.044.72a1.52 1.52 0 0 1-1.44-1.04L6 76.943l2.958-.879Z" fill="#669DF6"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html
index 2c458d6..f468303 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.html
+++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -192,16 +192,17 @@
     <cr-lazy-render id="confirmClearAllData">
       <template>
         <cr-dialog close-text="$i18n{close}">
-          <div slot="title">
-            $i18n{siteSettingsClearAllStorageDialogTitle}
-          </div>
+          <div slot="title">[[getClearAllStorageDialogTitle_(filter)]]</div>
           <div slot="body">
-            <div>[[getClearAllDataLabel_(totalUsage_)]]</div>
+            <div id="clearAllStorageDialogDescription">
+              [[getClearAllStorageDialogDescription_(totalUsage_,
+                  filteredList_)]]
+            </div>
             <div class="detail-list">
-              <div class="detail">
+              <div id="clearAllStorageDialogSignOutLabel" class="detail">
                 <iron-icon icon="all-sites:logout" aria-hidden="true"
                   role="presentation"></iron-icon>
-                $i18n{siteSettingsClearAllStorageSignOut}
+                [[getClearAllStorageDialogSignOutLabel_(filter)]]
               </div>
               <div class="detail">
                 <iron-icon icon="all-sites:offline" aria-hidden="true"
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.ts b/chrome/browser/resources/settings/site_settings/all_sites.ts
index ef56df7598..ac532d5 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.ts
+++ b/chrome/browser/resources/settings/site_settings/all_sites.ts
@@ -552,9 +552,9 @@
   }
 
   /**
-   * Selects the correct string to display for clear button based on whether a
-   * filter is applied.
-   * @return The correct |clearAllButton| string based on whether a filter
+   * Selects the appropriate string to display for clear button based on whether
+   * a filter is applied.
+   * @return The appropriate |clearAllButton| string based on whether a filter
    *     is applied.
    */
   private getClearDataButtonString_(): string {
@@ -565,9 +565,9 @@
   }
 
   /**
-   * Selects the correct string to display for total usage based on whether a
-   * filter is applied.
-   * @return The correct |clearLabel| string based on whether a filter
+   * Selects the appropriate string to display for total usage based on whether
+   * a filter is applied.
+   * @return The appropriate |clearLabel| string based on whether a filter
    *     is applied.
    */
   private getClearStorageDescription_(): string {
@@ -748,20 +748,55 @@
         .some(o => o.hasPermissionSettings);
   }
 
+
   /**
-   * Get the appropriate label for the clear all data confirmation
-   * dialog, depending on whether or not any apps are installed.
+   * Selects the appropriate title to display for clear storage confirmation
+   * dialog based on whether a filter is applied.
+   * @return The appropriate title for clear storage confirmation dialog.
    */
-  private getClearAllDataLabel_(): string {
+  private getClearAllStorageDialogTitle_(): string {
+    const titleId = this.isFiltered_() ?
+        'siteSettingsClearDisplayedStorageDialogTitle' :
+        'siteSettingsClearAllStorageDialogTitle';
+    return loadTimeData.substituteString(this.i18n(titleId), this.totalUsage_);
+  }
+
+  /**
+   * Get the appropriate label for the clear data confirmation dialog, depending
+   * on whether any apps are installed and/or filter is applied.
+   * @return The appropriate description for clear data confirmation dialog.
+   */
+  private getClearAllStorageDialogDescription_(): string {
     const anyAppsInstalled = this.filteredList_.some(g => g.hasInstalledPWA);
-    const messageId = anyAppsInstalled ?
-        'siteSettingsClearAllStorageConfirmationInstalled' :
-        'siteSettingsClearAllStorageConfirmation';
+    let messageId;
+    if (anyAppsInstalled) {
+      messageId = this.isFiltered_() ?
+          'siteSettingsClearDisplayedStorageConfirmationInstalled' :
+          'siteSettingsClearAllStorageConfirmationInstalled';
+    } else {
+      messageId = this.isFiltered_() ?
+          'siteSettingsClearDisplayedStorageConfirmation' :
+          'siteSettingsClearAllStorageConfirmation';
+    }
+
     return loadTimeData.substituteString(
         this.i18n(messageId), this.totalUsage_);
   }
 
   /**
+   * Selects the appropriate string to display for the sign-out string in
+   * confirmation popup based on whether a filter is applied.
+   * @return The appropriate sign out confirmation string based on whether a
+   *     filter is applied.
+   */
+  private getClearAllStorageDialogSignOutLabel_(): string {
+    const signOutLabelId = this.isFiltered_() ?
+        'siteSettingsClearDisplayedStorageSignOut' :
+        'siteSettingsClearAllStorageSignOut';
+    return this.i18n(signOutLabelId);
+  }
+
+  /**
    * Get the appropriate label for the clear data confirmation
    * dialog, depending on whether the user is clearing data for a
    * single origin or an entire site group.
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 8f2824e..9b7c1893 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -1376,6 +1376,10 @@
       "Sync.TrustedVaultDownloadKeysStatus",
       /*sample=*/syncer::TrustedVaultDownloadKeysStatus::kSuccess,
       /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "Sync.TrustedVaultURLFetchResponse.DownloadKeys",
+      /*sample=*/200,
+      /*expected_bucket_count=*/1);
 }
 
 // Regression test for crbug.com/1267391: after following key rotation the
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index e7d186e..575b70f8 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -277,10 +277,6 @@
         USB
       </message>
 
-      <message name="IDS_UNSUPPORTED" desc="Message displayed to the user when an attempted action is not supported.">
-        Unsupported
-      </message>
-
       <!-- Sign-in, sync and personalization preferences -->
       <message name="IDS_PREFS_SECTION_ACCOUNT_AND_GOOGLE_SERVICES" desc="Title for the group of account-related entries and google services in Settings. [CHAR_LIMIT=32]">
         You and Google
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNSUPPORTED.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNSUPPORTED.png.sha1
deleted file mode 100644
index 91c658f..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNSUPPORTED.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-eb5b8c28cd27b6caec73de2b8c5c9f6df916bd8e
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/app_list_test_util.cc b/chrome/browser/ui/app_list/app_list_test_util.cc
index 7eee25e..643bc848 100644
--- a/chrome/browser/ui/app_list/app_list_test_util.cc
+++ b/chrome/browser/ui/app_list/app_list_test_util.cc
@@ -8,7 +8,6 @@
 #include "base/files/file_path.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/web_applications/externally_managed_app_manager_impl.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/test_web_app_url_loader.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index f66efaa..0f7b8fb 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -65,6 +65,7 @@
 #include "chrome/browser/ash/login/demo_mode/demo_mode_test_helper.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/prefs/browser_prefs.h"
@@ -104,7 +105,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
@@ -534,8 +534,7 @@
   void StartWebAppProvider(Profile* profile) {
     auto* provider = web_app::FakeWebAppProvider::Get(profile);
 
-    auto* system_web_app_manager =
-        web_app::TestSystemWebAppManager::Get(profile);
+    auto* system_web_app_manager = ash::TestSystemWebAppManager::Get(profile);
 
     provider->SetRunSubsystemStartupTasks(true);
     provider->Start();
diff --git a/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc b/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
index 69cd5a8..ac8fcea9 100644
--- a/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
@@ -45,7 +45,6 @@
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_item_factory.h"
 #include "chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h"
 #include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h
index 8ca0b98..492523c 100644
--- a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h
+++ b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_controller.h
@@ -40,9 +40,9 @@
       (override));
   MOCK_METHOD(void, OnBasePromptChoiceSelected, (size_t), (override));
   MOCK_METHOD(void,
-              ShowGeneratedPasswordPrompt,
+              ShowUseGeneratedPasswordPrompt,
               (const autofill_assistant::password_change::
-                   GeneratedPasswordPromptSpecification&,
+                   UseGeneratedPasswordPromptSpecification&,
                const std::u16string&),
               (override));
   MOCK_METHOD(void, ShowStartingScreen, (const GURL&), (override));
diff --git a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_display.h b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_display.h
index 8d3afcb..375b23c4 100644
--- a/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_display.h
+++ b/chrome/browser/ui/autofill_assistant/password_change/mock_password_change_run_display.h
@@ -36,7 +36,7 @@
               (const std::vector<PromptChoice>&),
               (override));
   MOCK_METHOD(void,
-              ShowGeneratedPasswordPrompt,
+              ShowUseGeneratedPasswordPrompt,
               (const std::u16string&,
                const std::u16string&,
                const std::u16string&,
diff --git a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h
index 230de5a..7257836 100644
--- a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h
+++ b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_controller.h
@@ -53,9 +53,9 @@
   virtual void OnBasePromptChoiceSelected(size_t choice_index) = 0;
 
   // Shows a generated password prompt and receives the response from the view.
-  virtual void ShowGeneratedPasswordPrompt(
+  virtual void ShowUseGeneratedPasswordPrompt(
       const autofill_assistant::password_change::
-          GeneratedPasswordPromptSpecification& password_prompt,
+          UseGeneratedPasswordPromptSpecification& password_prompt,
       const std::u16string& suggested_password) = 0;
   // Called on user interaction with the prompt. `selected` indicates whether
   // the automatically generated password was selected or not.
diff --git a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h
index 0313a121..dce3ef5 100644
--- a/chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h
+++ b/chrome/browser/ui/autofill_assistant/password_change/password_change_run_display.h
@@ -54,7 +54,7 @@
   // Shows a generated password prompt for the password passed as a parameter.
   // Offers two buttons, one to accept the generated password and one to
   // choose manually. Relies on the controller calling `ClearPrompt` to close.
-  virtual void ShowGeneratedPasswordPrompt(
+  virtual void ShowUseGeneratedPasswordPrompt(
       const std::u16string& title,
       const std::u16string& generated_password,
       const std::u16string& description,
diff --git a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.cc b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.cc
index efd521f..867c3c1 100644
--- a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.cc
+++ b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.cc
@@ -172,7 +172,7 @@
   }
 }
 
-void PasswordChangeRunView::ShowGeneratedPasswordPrompt(
+void PasswordChangeRunView::ShowUseGeneratedPasswordPrompt(
     const std::u16string& title,
     const std::u16string& suggested_password,
     const std::u16string& description,
diff --git a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.h b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.h
index 550a28f..7c435b2 100644
--- a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.h
+++ b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view.h
@@ -62,7 +62,7 @@
   void SetProgressBarStep(
       autofill_assistant::password_change::ProgressStep progress_step) override;
   void ShowBasePrompt(const std::vector<PromptChoice>& options) override;
-  void ShowGeneratedPasswordPrompt(
+  void ShowUseGeneratedPasswordPrompt(
       const std::u16string& title,
       const std::u16string& generated_password,
       const std::u16string& description,
diff --git a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view_unittest.cc b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view_unittest.cc
index df0d2388..68cc293 100644
--- a/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view_unittest.cc
+++ b/chrome/browser/ui/views/autofill_assistant/password_change/password_change_run_view_unittest.cc
@@ -144,8 +144,8 @@
 
 TEST_F(PasswordChangeRunViewTest, CreateSuggestedPasswordPromptAndAccept) {
   std::vector<PromptChoice> choices = CreatePromptChoices();
-  view()->ShowGeneratedPasswordPrompt(kTitle, kPassword, kDescription,
-                                      choices[0], choices[1]);
+  view()->ShowUseGeneratedPasswordPrompt(kTitle, kPassword, kDescription,
+                                         choices[0], choices[1]);
 
   views::View* button_container = GetButtonContainer();
   ASSERT_TRUE(button_container);
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index 441a3ddf..8e66e07 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -64,7 +64,7 @@
 
 // This is not quite right but is the closest server type that wasn't already
 // used.
-const auto kBillingAddressType = autofill::ADDRESS_BILLING_LINE1;
+const auto kBillingAddressType = autofill::ADDRESS_HOME_LINE1;
 
 bool IsCardExpired(const std::u16string& month,
                    const std::u16string& year,
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
index 314fd95..09160bb 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
@@ -485,7 +485,7 @@
 
   // Proper error shown.
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_BILLING_ADDRESS_REQUIRED),
-            GetErrorLabelForType(autofill::ADDRESS_BILLING_LINE1));
+            GetErrorLabelForType(autofill::ADDRESS_HOME_LINE1));
 
   // Fixing the billing address.
   SelectBillingAddress(billing_profile.guid());
@@ -655,9 +655,8 @@
   ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
                                 DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW);
   // Billing address combobox must be disabled since there are no saved address.
-  views::View* billing_address_combobox =
-      dialog_view()->GetViewByID(EditorViewController::GetInputFieldViewId(
-          autofill::ADDRESS_BILLING_LINE1));
+  views::View* billing_address_combobox = dialog_view()->GetViewByID(
+      EditorViewController::GetInputFieldViewId(autofill::ADDRESS_HOME_LINE1));
   ASSERT_NE(nullptr, billing_address_combobox);
   EXPECT_FALSE(billing_address_combobox->GetEnabled());
 
@@ -690,7 +689,7 @@
   // The billing address must be properly selected and valid.
   views::Combobox* billing_combobox = static_cast<views::Combobox*>(
       dialog_view()->GetViewByID(EditorViewController::GetInputFieldViewId(
-          autofill::ADDRESS_BILLING_LINE1)));
+          autofill::ADDRESS_HOME_LINE1)));
   ASSERT_NE(nullptr, billing_combobox);
   EXPECT_FALSE(billing_combobox->GetInvalid());
   EXPECT_TRUE(billing_combobox->GetEnabled());
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
index 73d7779..6721266 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -55,8 +55,6 @@
 namespace payments {
 
 namespace {
-const auto kBillingAddressType = autofill::ADDRESS_BILLING_LINE1;
-
 // This is preferred to SelectValue, since only SetSelectedRow fires the events
 // as if done by a user.
 void SelectComboboxRowForValue(views::Combobox* combobox,
@@ -791,9 +789,9 @@
 
 void PaymentRequestBrowserTestBase::SelectBillingAddress(
     const std::string& billing_address_id) {
-  views::Combobox* address_combobox(
-      static_cast<views::Combobox*>(dialog_view()->GetViewByID(
-          EditorViewController::GetInputFieldViewId(kBillingAddressType))));
+  views::Combobox* address_combobox(static_cast<views::Combobox*>(
+      dialog_view()->GetViewByID(EditorViewController::GetInputFieldViewId(
+          autofill::ADDRESS_HOME_LINE1))));
   ASSERT_NE(address_combobox, nullptr);
   autofill::AddressComboboxModel* address_combobox_model(
       static_cast<autofill::AddressComboboxModel*>(
diff --git a/chrome/browser/ui/views/tabs/tab_container.cc b/chrome/browser/ui/views/tabs/tab_container.cc
index 7e58a7e..0cabdf4 100644
--- a/chrome/browser/ui/views/tabs/tab_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_container.cc
@@ -505,11 +505,8 @@
     group_views->second->UpdateBounds();
 }
 
-// TODO(pkasting): This should really return an optional<size_t>
 int TabContainer::GetModelIndexOf(const TabSlotView* slot_view) const {
-  const absl::optional<size_t> index =
-      tabs_view_model_.GetIndexOfView(slot_view);
-  return index.has_value() ? static_cast<int>(index.value()) : -1;
+  return tabs_view_model_.GetIndexOfView(slot_view);
 }
 
 Tab* TabContainer::GetTabAtModelIndex(int index) const {
@@ -566,9 +563,9 @@
 
   // A hit on the tab is not in the caption unless it is in the thin strip
   // mentioned above.
-  const absl::optional<size_t> tab_index = tabs_view_model_.GetIndexOfView(v);
-  if (tab_index.has_value() && IsValidModelIndex(tab_index.value())) {
-    Tab* tab = GetTabAtModelIndex(tab_index.value());
+  const int tab_index = tabs_view_model_.GetIndexOfView(v);
+  if (IsValidModelIndex(tab_index)) {
+    Tab* tab = GetTabAtModelIndex(tab_index);
     gfx::Rect tab_drag_handle = tab->GetMirroredBounds();
     tab_drag_handle.set_height(drag_handle_extension);
     return extend_drag_handle && !tab->IsActive() &&
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index d110b49..4a7ea25 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2095,8 +2095,8 @@
 }
 
 void TabStrip::OnViewFocused(views::View* observed_view) {
-  auto index = tab_container_->tabs_view_model()->GetIndexOfView(observed_view);
-  if (index.has_value())
+  int index = tab_container_->tabs_view_model()->GetIndexOfView(observed_view);
+  if (index != -1)
     controller_->OnKeyboardFocusedTabChanged(index);
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
index 507a3e78..0beed83 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
@@ -93,9 +93,9 @@
   return views;
 }
 
-size_t TabStripLayoutHelper::GetPinnedTabCount() const {
+int TabStripLayoutHelper::GetPinnedTabCount() const {
   views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
-  size_t pinned_count = 0;
+  int pinned_count = 0;
   while (pinned_count < tabs->view_size() &&
          tabs->view_at(pinned_count)->data().pinned) {
     pinned_count++;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
index bc0b9c3..6401ae89 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
@@ -54,7 +54,7 @@
   int first_non_pinned_tab_x() { return first_non_pinned_tab_x_; }
 
   // Returns the number of pinned tabs in the tabstrip.
-  size_t GetPinnedTabCount() const;
+  int GetPinnedTabCount() const;
 
   // Returns a map of all tab groups and their bounds.
   const std::map<tab_groups::TabGroupId, gfx::Rect>& group_header_ideal_bounds()
diff --git a/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/network_settings_dialog.cc b/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/network_settings_dialog.cc
index ed733b9..12758b0 100644
--- a/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/network_settings_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/network_settings_dialog.cc
@@ -73,7 +73,7 @@
   void ShowManageCerts() {
     // Dialogs manage their own lifecycle and will delete themselves.
     CertificateManagerDialog* dialog = new CertificateManagerDialog(
-        ProfileManager::GetActiveUserProfile(), nullptr, nullptr);
+        ProfileManager::GetActiveUserProfile(), nullptr);
     dialog->Show();
   }
 };
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
index 437be2715..95b7e1b 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -6,7 +6,6 @@
 
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/screens/error_screen.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -22,35 +21,9 @@
 ErrorScreenHandler::~ErrorScreenHandler() = default;
 
 void ErrorScreenHandler::Show() {
-  if (!IsJavascriptAllowed()) {
-    show_on_init_ = true;
-    return;
-  }
-
   base::Value::Dict data;
   data.Set("hasUserPods", ash::LoginDisplayHost::default_host()->HasUserPods());
   ShowInWebUI(std::move(data));
-
-  if (screen_)
-    screen_->DoShow();
-  showing_ = true;
-}
-
-void ErrorScreenHandler::Hide() {
-  showing_ = false;
-  show_on_init_ = false;
-  if (screen_)
-    screen_->DoHide();
-}
-
-void ErrorScreenHandler::Bind(ErrorScreen* screen) {
-  screen_ = screen;
-  BaseScreenHandler::SetBaseScreenDeprecated(screen_);
-}
-
-void ErrorScreenHandler::Unbind() {
-  screen_ = nullptr;
-  BaseScreenHandler::SetBaseScreenDeprecated(nullptr);
 }
 
 void ErrorScreenHandler::ShowOobeScreen(OobeScreenId screen) {
@@ -132,15 +105,4 @@
   builder->Add("offlineLogin", IDS_OFFLINE_LOGIN_HTML);
 }
 
-void ErrorScreenHandler::InitializeDeprecated() {
-  if (!IsJavascriptAllowed())
-    return;
-
-  if (show_on_init_) {
-    // TODO(nkostylev): Check that context initial state is properly passed.
-    Show();
-    show_on_init_ = false;
-  }
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
index 83d5743..587dccf 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.h
@@ -9,10 +9,6 @@
 #include "chrome/browser/ash/login/screens/network_error.h"
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 
-namespace ash {
-class ErrorScreen;
-}
-
 namespace chromeos {
 
 // Interface for dependency injection between ErrorScreen and its actual
@@ -27,15 +23,6 @@
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
-  // Hides the contents of the screen.
-  virtual void Hide() = 0;
-
-  // Binds `screen` to the view.
-  virtual void Bind(ash::ErrorScreen* screen) = 0;
-
-  // Unbinds the screen from the view.
-  virtual void Unbind() = 0;
-
   // Switches to `screen`.
   virtual void ShowOobeScreen(OobeScreenId screen) = 0;
 
@@ -76,9 +63,6 @@
  private:
   // ErrorScreenView:
   void Show() override;
-  void Hide() override;
-  void Bind(ash::ErrorScreen* screen) override;
-  void Unbind() override;
   void ShowOobeScreen(OobeScreenId screen) override;
   void SetErrorStateCode(NetworkError::ErrorState error_state) override;
   void SetErrorStateNetwork(const std::string& network_name) override;
@@ -91,20 +75,10 @@
   // BaseScreenHandler:
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
-  void InitializeDeprecated() override;
 
   // WebUI message handlers.
   void HandleHideCaptivePortal();
 
-  // Non-owning ptr.
-  ash::ErrorScreen* screen_ = nullptr;
-
-  // Should the screen be shown right after initialization?
-  bool show_on_init_ = false;
-
-  // Whether the error screen is currently shown.
-  bool showing_ = false;
-
   base::WeakPtrFactory<ErrorScreenHandler> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 7358373..085fc93 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -137,34 +137,15 @@
 
 namespace {
 
-bool IsOnline(NetworkStateInformer::State state,
-              NetworkError::ErrorReason reason) {
-  return state == NetworkStateInformer::ONLINE &&
-         reason != NetworkError::ERROR_REASON_PORTAL_DETECTED &&
-         reason != NetworkError::ERROR_REASON_LOADING_TIMEOUT;
-}
-
-bool IsBehindCaptivePortal(NetworkStateInformer::State state,
-                           NetworkError::ErrorReason reason) {
-  return state == NetworkStateInformer::CAPTIVE_PORTAL ||
-         reason == NetworkError::ERROR_REASON_PORTAL_DETECTED;
-}
-
 bool IsProxyError(NetworkStateInformer::State state,
                   NetworkError::ErrorReason reason,
                   net::Error frame_error) {
-  return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
-         reason == NetworkError::ERROR_REASON_PROXY_AUTH_CANCELLED ||
-         reason == NetworkError::ERROR_REASON_PROXY_CONNECTION_FAILED ||
+  return NetworkStateInformer::IsProxyError(state, reason) ||
          (reason == NetworkError::ERROR_REASON_FRAME_ERROR &&
           (frame_error == net::ERR_PROXY_CONNECTION_FAILED ||
            frame_error == net::ERR_TUNNEL_CONNECTION_FAILED));
 }
 
-bool IsSigninScreen(const OobeScreenId screen) {
-  return screen == GaiaView::kScreenId;
-}
-
 }  // namespace
 
 // SigninScreenHandler implementation ------------------------------------------
@@ -289,8 +270,9 @@
   }
   connecting_callback_.Cancel();
 
-  const bool is_online = IsOnline(state, reason);
-  const bool is_behind_captive_portal = IsBehindCaptivePortal(state, reason);
+  const bool is_online = NetworkStateInformer::IsOnline(state, reason);
+  const bool is_behind_captive_portal =
+      NetworkStateInformer::IsBehindCaptivePortal(state, reason);
   const bool is_gaia_loading_timeout =
       (reason == NetworkError::ERROR_REASON_LOADING_TIMEOUT);
   const bool is_gaia_error =
@@ -314,8 +296,7 @@
   // Hide offline message (if needed) and return if current screen is
   // not a Gaia frame.
   if (!is_gaia_signin) {
-    if (!IsSigninScreenHiddenByError())
-      HideOfflineMessage(state, reason);
+    HideOfflineMessage(state, reason);
     return;
   }
 
@@ -374,7 +355,7 @@
 
 void SigninScreenHandler::HideOfflineMessage(NetworkStateInformer::State state,
                                              NetworkError::ErrorReason reason) {
-  if (!IsSigninScreenHiddenByError())
+  if (!IsGaiaHiddenByError())
     return;
 
   gaia_reload_reason_ = NetworkError::ERROR_REASON_NONE;
@@ -448,16 +429,12 @@
 }
 
 bool SigninScreenHandler::IsGaiaVisible() {
-  return IsSigninScreen(GetCurrentScreen());
+  return GetCurrentScreen() == GaiaView::kScreenId;
 }
 
 bool SigninScreenHandler::IsGaiaHiddenByError() {
-  return IsSigninScreenHiddenByError();
-}
-
-bool SigninScreenHandler::IsSigninScreenHiddenByError() {
   return (GetCurrentScreen() == ErrorScreenView::kScreenId) &&
-         (IsSigninScreen(error_screen_->GetParentScreen()));
+         (error_screen_->GetParentScreen() == GaiaView::kScreenId);
 }
 
 net::Error SigninScreenHandler::FrameError() const {
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index f82446761..f712c161 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -103,8 +103,6 @@
   friend class ReportDnsCacheClearedOnUIThread;
 
   void UpdateStateInternal(NetworkError::ErrorReason reason, bool force_update);
-  void SetupAndShowOfflineMessage(NetworkStateInformer::State state,
-                                  NetworkError::ErrorReason reason);
   void HideOfflineMessage(NetworkStateInformer::State state,
                           NetworkError::ErrorReason reason);
   void ReloadGaia(bool force_reload);
@@ -131,10 +129,6 @@
   // Gaia sign-in page.
   bool IsGaiaHiddenByError();
 
-  // Returns true if current screen is the error screen over signin
-  // screen.
-  bool IsSigninScreenHiddenByError();
-
   net::Error FrameError() const;
 
 
diff --git a/chrome/browser/ui/webui/chromeos/login/theme_selection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/theme_selection_screen_handler.cc
index 0b8f4fc..49e9d60 100644
--- a/chrome/browser/ui/webui/chromeos/login/theme_selection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/theme_selection_screen_handler.cc
@@ -12,6 +12,7 @@
 #include "components/login/localized_values_builder.h"
 
 namespace chromeos {
+
 constexpr StaticOobeScreenId ThemeSelectionScreenView::kScreenId;
 
 ThemeSelectionScreenHandler::ThemeSelectionScreenHandler()
@@ -26,8 +27,10 @@
 void ThemeSelectionScreenHandler::DeclareLocalizedValues(
     ::login::LocalizedValuesBuilder* builder) {
   builder->Add("themeSelectionScreenTitle", IDS_THEME_SELECTION_TITLE);
-  builder->Add("themeSelectionScreenDescription",
-               IDS_THEME_SELECTION_DESCRIPTION);
+  builder->Add("themeSelectionScreenDescriptionClamshell",
+               IDS_THEME_SELECTION_DESCRIPTION_CLAMSHELL);
+  builder->Add("themeSelectionScreenDescriptionTablet",
+               IDS_THEME_SELECTION_DESCRIPTION_TABLET);
   builder->Add("lightThemeLabel", IDS_THEME_LIGHT_LABEL);
   builder->Add("lightThemeDescription", IDS_THEME_LIGHT_DESCRIPTION);
   builder->Add("darkThemeLabel", IDS_THEME_DARK_LABEL);
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index fed79e7..be0d0a40 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2407,6 +2407,8 @@
     {"siteSettingsDelete", IDS_SETTINGS_SITE_SETTINGS_DELETE},
     {"siteSettingsClearAllStorageDialogTitle",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DIALOG_TITLE},
+    {"siteSettingsClearDisplayedStorageDialogTitle",
+     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_DIALOG_TITLE},
     {"siteSettingsClearAllStorageDescription",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_DESCRIPTION},
     {"siteSettingsClearDisplayedStorageDescription",
@@ -2417,10 +2419,16 @@
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_LABEL},
     {"siteSettingsClearAllStorageConfirmation",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION},
+    {"siteSettingsClearDisplayedStorageConfirmation",
+     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION},
     {"siteSettingsClearAllStorageConfirmationInstalled",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_CONFIRMATION_INSTALLED},
+    {"siteSettingsClearDisplayedStorageConfirmationInstalled",
+     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_CONFIRMATION_INSTALLED},
     {"siteSettingsClearAllStorageSignOut",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_ALL_STORAGE_SIGN_OUT},
+    {"siteSettingsClearDisplayedStorageSignOut",
+     IDS_SETTINGS_SITE_SETTINGS_CLEAR_DISPLAYED_STORAGE_SIGN_OUT},
     {"siteSettingsOriginDeleteConfirmation",
      IDS_SETTINGS_SITE_SETTINGS_ORIGIN_DELETE_CONFIRMATION},
     {"siteSettingsOriginDeleteConfirmationInstalled",
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 918027f..4bf93d4 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -388,8 +388,6 @@
   sources = [
     # TODO(crbug.com/1321984): Move these files to
     # chrome/browser/ash/system_web_apps/test_support/ directory.
-    "system_web_apps/test/test_system_web_app_manager.cc",
-    "system_web_apps/test/test_system_web_app_manager.h",
     "system_web_apps/test/test_system_web_app_url_data_source.cc",
     "system_web_apps/test/test_system_web_app_url_data_source.h",
     "system_web_apps/test/test_system_web_app_web_ui_controller_factory.cc",
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index f12626c..d2f5425f 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -18,6 +18,7 @@
 #include "base/containers/extend.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_base.h"
@@ -54,7 +55,6 @@
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/services/app_service/public/cpp/intent_filter.h"
@@ -63,6 +63,7 @@
 #include "components/services/app_service/public/cpp/run_on_os_login_types.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/browser/clear_site_data_utils.h"
+#include "content/public/common/content_features.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/display/types/display_constants.h"
 #include "url/gurl.h"
@@ -226,6 +227,12 @@
   return web_app.note_taking_new_note_url().is_valid();
 }
 
+bool IsLockScreenCapable(const web_app::WebApp& web_app) {
+  if (!base::FeatureList::IsEnabled(features::kWebLockScreenApi))
+    return false;
+  return web_app.lock_screen_start_url().is_valid();
+}
+
 }  // namespace
 
 void UninstallImpl(WebAppProvider* provider,
@@ -561,9 +568,6 @@
           provider_->os_integration_manager().GetEnabledFileHandlers(
               web_app->app_id())));
 
-  if (IsNoteTakingWebApp(*web_app))
-    app->intent_filters.push_back(apps_util::CreateNoteTakingFilter());
-
   // These filters are used by the settings page to display would-be-handled
   // extensions even when the feature is not enabled for the app, whereas
   // `GetEnabledFileHandlers` above only returns the ones that currently are
@@ -581,6 +585,12 @@
             {extensions_set.begin(), extensions_set.end()})));
   }
 
+  if (IsNoteTakingWebApp(*web_app))
+    app->intent_filters.push_back(apps_util::CreateNoteTakingFilter());
+
+  if (IsLockScreenCapable(*web_app))
+    app->intent_filters.push_back(apps_util::CreateLockScreenFilter());
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (web_app->app_id() == crostini::kCrostiniTerminalSystemAppId) {
     app->intent_filters.push_back(apps::ConvertMojomIntentFilterToIntentFilter(
@@ -674,6 +684,9 @@
   if (IsNoteTakingWebApp(*web_app))
     app->intent_filters.push_back(apps_util::CreateNoteTakingFilterMojom());
 
+  if (IsLockScreenCapable(*web_app))
+    app->intent_filters.push_back(apps_util::CreateLockScreenFilterMojom());
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (web_app->app_id() == crostini::kCrostiniTerminalSystemAppId) {
     app->intent_filters.push_back(apps_util::CreateFileFilter(
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index 059f606..9bc02ed 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -57,6 +57,7 @@
   deps = [
     ":extensions",
     "//build:chromeos_buildflags",
+    "//chrome/browser/ash/system_web_apps/test_support",
     "//chrome/browser/web_applications:web_applications",
     "//chrome/browser/web_applications:web_applications_test_support",
     "//chrome/common",
diff --git a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
index d51b4c6..12375e1e 100644
--- a/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/web_app_policy_manager_unittest.cc
@@ -18,11 +18,11 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/app_registrar_observer.h"
 #include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_registry_controller.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
@@ -330,7 +330,7 @@
     fake_externally_managed_app_manager_ =
         std::make_unique<FakeExternallyManagedAppManager>(profile());
     test_system_app_manager_ =
-        std::make_unique<web_app::TestSystemWebAppManager>(profile());
+        std::make_unique<ash::TestSystemWebAppManager>(profile());
     web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile());
 
     controller().SetUp(profile());
@@ -456,7 +456,7 @@
     return *fake_externally_managed_app_manager_;
   }
 
-  TestSystemWebAppManager& system_app_manager() {
+  ash::TestSystemWebAppManager& system_app_manager() {
     return *test_system_app_manager_;
   }
 
@@ -518,7 +518,7 @@
       externally_installed_app_prefs_;
   std::unique_ptr<FakeExternallyManagedAppManager>
       fake_externally_managed_app_manager_;
-  std::unique_ptr<TestSystemWebAppManager> test_system_app_manager_;
+  std::unique_ptr<ash::TestSystemWebAppManager> test_system_app_manager_;
   std::unique_ptr<WebAppPolicyManager> web_app_policy_manager_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 9d20cd1..b5beb1f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1656028705-702b302b60ec01dcdaef25b7de6a0f47efe45929.profdata
+chrome-linux-main-1656050073-99a9cde3c0775f914ecf099d4b963015d843ec76.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 97676d6f..ed45a80 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1656028705-262d9f1fb68ab007552b222f4ff81738057259dd.profdata
+chrome-mac-main-1656069731-48361712dec9958ac5c6782018f3a07fddbbf759.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c535939..1eb9241 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1656017500-8f39b824d1281af3b4666134c0a8c15be51c3b6b.profdata
+chrome-win32-main-1656059247-aca39825162226d1b69841cf1d93c7b45da4ccbf.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 557a4d6..52560e0 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1656017500-b73f270d663b35730eead36cc01efdf215c6f35d.profdata
+chrome-win64-main-1656059247-964617ab74d0c288e575deaceb5bee87f13675b1.profdata
diff --git a/chrome/common/extensions/api/input_method_private.json b/chrome/common/extensions/api/input_method_private.json
index df4f49fb5..370fdab 100644
--- a/chrome/common/extensions/api/input_method_private.json
+++ b/chrome/common/extensions/api/input_method_private.json
@@ -839,6 +839,24 @@
     ],
     "events": [
       {
+        "name": "onCaretBoundsChanged",
+        "type": "function",
+        "description": "Fired when the caret bounds change.",
+        "parameters": [
+          {
+            "name": "caretBounds",
+            "type": "object",
+            "description": "The bounds information for the caret.",
+            "properties": {
+              "x": {"type": "integer"},
+              "y": {"type": "integer"},
+              "w": {"type": "integer"},
+              "h": {"type": "integer"}
+            }
+          }
+        ]
+      },
+      {
         "name": "onChanged",
         "type": "function",
         "description": "Fired when the input method is changed.",
diff --git a/chrome/renderer/resources/extensions/remote_apps/OWNERS b/chrome/renderer/resources/extensions/remote_apps/OWNERS
index fc0c3ac..aef9c2c 100644
--- a/chrome/renderer/resources/extensions/remote_apps/OWNERS
+++ b/chrome/renderer/resources/extensions/remote_apps/OWNERS
@@ -1,2 +1 @@
 hendrich@chromium.org
-jityao@google.com
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index bcd1579..ddcfec00 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2639,6 +2639,7 @@
         "../browser/lacros/app_mode/kiosk_session_service_browsertest.cc",
         "../browser/lacros/lacros_extension_apps_controller_browsertest.cc",
         "../browser/lacros/lacros_extension_apps_publisher_browsertest.cc",
+        "../browser/policy/restricted_mgs_policy_provider_lacros_browsertest.cc",
         "../browser/ui/sharing_hub/sharing_hub_bubble_controller_chromeos_lacros_browsertest.cc",
         "../browser/web_applications/app_service/lacros_web_apps_controller_browsertest.cc",
       ]
@@ -3834,6 +3835,7 @@
         "../browser/notifications/notifier_settings_test_observer.cc",
         "../browser/notifications/notifier_settings_test_observer.h",
         "../browser/policy/login_policy_test_base_browsertest.cc",
+        "../browser/policy/restricted_mgs_policy_provider_ash_browsertest.cc",
         "../browser/process_singleton_browsertest.cc",
         "../browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc",
         "../browser/sessions/session_restore_browsertest_chromeos.cc",
@@ -6199,6 +6201,7 @@
       "../browser/download/android/available_offline_content_provider_unittest.cc",
       "../browser/download/android/download_manager_service_unittest.cc",
       "../browser/enterprise/util/android_enterprise_info_unittest.cc",
+      "../browser/fast_checkout/fast_checkout_client_impl_unittest.cc",
       "../browser/long_screenshots/long_screenshots_tab_service_unittest.cc",
       "../browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc",
       "../browser/metrics/chrome_android_metrics_provider_unittest.cc",
@@ -6256,6 +6259,7 @@
       "//chrome/browser/supervised_user/kids_chrome_management:proto",
       "//chrome/browser/thumbnail:unit_tests",
       "//chrome/services/media_gallery_util:unit_tests",
+      "//components/autofill_assistant/browser/public:unit_test_support",
       "//components/back_forward_cache",
       "//components/crash/content/browser",
       "//components/device_reauth",
@@ -7353,6 +7357,7 @@
       "../browser/lacros/sync/sync_explicit_passphrase_client_lacros_unittest.cc",
       "../browser/metrics/lacros_metrics_provider_unittest.cc",
       "../browser/notifications/notification_platform_bridge_lacros_unittest.cc",
+      "../browser/policy/restricted_mgs_policy_provider_lacros_unittest.cc",
       "../browser/upgrade_detector/get_installed_version_lacros_unittest.cc",
       "../common/chrome_paths_lacros_unittest.cc",
       "../common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc",
@@ -7874,6 +7879,7 @@
         "../browser/extensions/system_display/display_info_provider_chromeos_unittest.cc",
         "../browser/extensions/system_display/system_display_serialization_unittest.cc",
         "../browser/media_galleries/chromeos/mtp_device_object_enumerator_unittest.cc",
+        "../browser/policy/restricted_mgs_policy_provider_ash_unittest.cc",
         "../browser/ui/webui/about_ui_unittest.cc",
         "../common/chromeos/extensions/chromeos_system_extension_info_unittest.cc",
         "../common/chromeos/extensions/manifest_tests/extension_manifests_chromeos_system_extension_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/BookmarkTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/BookmarkTestRule.java
index d4e7f073..c5ee176 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/BookmarkTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/BookmarkTestRule.java
@@ -14,7 +14,7 @@
 
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.bookmarks.BookmarkActivity;
+import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index c30a0e8..642f620 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -109,11 +109,11 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager_factory.h"
+#include "chrome/browser/ash/system_web_apps/test_support/test_system_web_app_manager.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_provider_factory.h"
 #include "components/guest_view/browser/guest_view_manager.h"
@@ -416,8 +416,7 @@
   web_app::WebAppProviderFactory::GetInstance()->SetTestingFactory(
       this, base::BindRepeating(&web_app::FakeWebAppProvider::BuildDefault));
   ash::SystemWebAppManagerFactory::GetInstance()->SetTestingFactory(
-      this,
-      base::BindRepeating(&web_app::TestSystemWebAppManager::BuildDefault));
+      this, base::BindRepeating(&ash::TestSystemWebAppManager::BuildDefault));
 #endif
 
   // Prefs for incognito profiles are set in CreateIncognitoPrefService().
diff --git a/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/manifest.json b/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/manifest.json
new file mode 100644
index 0000000..7575bba
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/manifest.json
@@ -0,0 +1,21 @@
+{
+    "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDfX9dHNh948bt00YhZBm3P6E5QLaOt+v8kXVtibQfiPtOD2FTScB/f0wX/EQWVO7BkaSOsRkTPcPIgocyMPYr2FLgqGLFlYT9nQpKJZUFNF5oJ5rG6Nv7ppf4zEB3j6da1IBRTz2yOZ+6O1TMZxol/V62/QcqrJeggsHTEPGLdr9Ua4b1Ka0xKJnJngZljsbw93FI1o+P9dAh5BS6wTPiZI/vmJVjvMTkSTnaZ3n9Go2t7A0XLcSxLcVyuLAd2mAvSN0mIviOukdM66wr7llif71nKuUt+4qvlr/r9HfwzN6pA4jkwhtS1UD+3CmB+wsHwsnohNcuu4FIQ6rgq/7QIDAQAB",
+    "name": "chrome.fileManagerPrivate tests",
+    "version": "0.1",
+    "manifest_version": 2,
+    "description": "Tests of chrome.fileManagerPrivate getDlpMetadata",
+    "app": {
+      "background": {
+        "scripts": [
+          "test.js"
+        ]
+      }
+    },
+    "permissions": [
+      "fileManagerPrivate",
+      {
+        "fileSystem": ["requestFileSystem"]
+      },
+      "unlimitedStorage"
+    ]
+  }
diff --git a/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/test.js b/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/test.js
new file mode 100644
index 0000000..b44946a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/dlp_metadata/test.js
@@ -0,0 +1,83 @@
+// 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.
+
+/**
+ * Gets the metadata of a volume having a specified type.
+ * @param {string} volumeType volume type for entry.
+ * @return {!Promise<chrome.fileManagerPrivate.VolumeMetadata>} Volume metadata.
+ */
+async function getVolumeMetadataByType(volumeType) {
+  return new Promise(
+      (resolve,
+       reject) => {chrome.fileManagerPrivate.getVolumeMetadataList(list => {
+        if (chrome.runtime.lastError) {
+          reject(chrome.runtime.lastError.message);
+          return;
+        }
+        resolve(list.find(v => v.volumeType === volumeType));
+      })});
+}
+
+/**
+ * Gets the file system of specific volume type.
+ * @param {string} volumeType volume type.
+ * @return {!Promise<chrome.fileManagerPrivate.FileSystem>} Volume metadata.
+ */
+async function getFileSystem(volumeType) {
+  const volume = await getVolumeMetadataByType(volumeType);
+  return new Promise((resolve, reject) => {
+    chrome.fileSystem.requestFileSystem({volumeId: volume.volumeId}, fs => {
+      if (chrome.runtime.lastError) {
+        reject(chrome.runtime.lastError.message);
+        return;
+      }
+      resolve(fs);
+    });
+  });
+}
+
+/**
+ * Gets an external file entry from a specified path.
+ * @param {string} volumeType volume type for entry.
+ * @param {string} path path of entry.
+ * @return {!Promise<Entry>} specified entry.
+ */
+async function getFileEntry(volumeType, path) {
+  const fs = await getFileSystem(volumeType);
+  return new Promise(resolve => {
+    fs.root.getFile(path, {}, entry => {
+      chrome.fileManagerPrivate.resolveIsolatedEntries(
+          [entry], externalEntries => {
+            resolve(externalEntries[0]);
+          });
+    });
+  });
+}
+
+/**
+ * Wrapper around getFileEntry() that resolves multiple paths.
+ * @param {string}  volumeType
+ * @param {Array<string>} paths
+ * @return {!Promise<Array<Entry>>}
+ */
+async function getFileEntries(volumeType, paths) {
+  return Promise.all(paths.map(path => getFileEntry(volumeType, path)));
+}
+
+chrome.test.runTests([async function getDlpMetadata() {
+  const testEntries = await getFileEntries(
+      'testing',
+      ['blocked_file.txt', 'unrestricted_file.txt', 'untracked_file.txt']);
+  chrome.test.assertEq(3, testEntries.length);
+  chrome.fileManagerPrivate.getDlpMetadata(
+      testEntries, chrome.test.callbackPass(dlpMetadata => {
+        chrome.test.assertEq(
+            [
+              {isDlpRestricted: true, sourceUrl: 'https://example1.com'},
+              {isDlpRestricted: false, sourceUrl: 'https://example2.com'},
+              {isDlpRestricted: false, sourceUrl: ''}
+            ],
+            dlpMetadata);
+      }))
+}]);
diff --git a/chrome/test/data/webui/settings/all_sites_tests.ts b/chrome/test/data/webui/settings/all_sites_tests.ts
index f6c30965..f23b9910 100644
--- a/chrome/test/data/webui/settings/all_sites_tests.ts
+++ b/chrome/test/data/webui/settings/all_sites_tests.ts
@@ -264,6 +264,121 @@
         clearLabel.innerText.trim());
   });
 
+  test('dynamic filtered clear data confirmation strings', async function() {
+    interface DialogState {
+      filter: boolean;
+      appInstalled: boolean;
+      storage: boolean;
+      title: string;
+      description: string;
+      signout: string;
+    }
+
+    const dialogStates: DialogState[] = [
+      {
+        filter: false,
+        appInstalled: false,
+        storage: true,
+        title: 'siteSettingsClearAllStorageDialogTitle',
+        description: 'siteSettingsClearAllStorageConfirmation',
+        signout: 'siteSettingsClearAllStorageSignOut'
+      },
+      {
+        filter: false,
+        appInstalled: true,
+        storage: true,
+        title: 'siteSettingsClearAllStorageDialogTitle',
+        description: 'siteSettingsClearAllStorageConfirmationInstalled',
+        signout: 'siteSettingsClearAllStorageSignOut'
+      },
+      {
+        filter: true,
+        appInstalled: false,
+        storage: true,
+        title: 'siteSettingsClearDisplayedStorageDialogTitle',
+        description: 'siteSettingsClearDisplayedStorageConfirmation',
+        signout: 'siteSettingsClearDisplayedStorageSignOut'
+      },
+      {
+        filter: true,
+        appInstalled: false,
+        storage: false,
+        title: 'siteSettingsClearDisplayedStorageDialogTitle',
+        description: 'siteSettingsClearDisplayedStorageConfirmation',
+        signout: 'siteSettingsClearDisplayedStorageSignOut'
+      },
+      {
+        filter: true,
+        appInstalled: true,
+        storage: false,
+        title: 'siteSettingsClearDisplayedStorageDialogTitle',
+        description: 'siteSettingsClearDisplayedStorageConfirmationInstalled',
+        signout: 'siteSettingsClearDisplayedStorageSignOut'
+      },
+      {
+        filter: true,
+        appInstalled: true,
+        storage: true,
+        title: 'siteSettingsClearDisplayedStorageDialogTitle',
+        description: 'siteSettingsClearDisplayedStorageConfirmationInstalled',
+        signout: 'siteSettingsClearDisplayedStorageSignOut'
+      },
+    ];
+
+    setUpAllSites(prefsVarious);
+    testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
+    await browserProxy.whenCalled('getAllSites');
+    // Removing extra unwanted site entries.
+    testElement.siteGroupMap.delete('google.com');
+
+    const clearAllButton =
+        testElement.$.clearAllButton.querySelector('cr-button')!;
+    const confirmClearAllData = testElement.$.confirmClearAllData.get();
+
+    // Open the clear all data dialog.
+    assertFalse(confirmClearAllData.open, 'closed dialog');
+    clearAllButton.click();
+    assertTrue(confirmClearAllData.open, 'open dialog');
+
+    for (const state of dialogStates) {
+      assertTrue(state.filter || state.storage, 'valid state');
+
+      if (state.storage) {  // 'bar' has storage of 100 B
+        testElement.filter = state.filter ? 'bar' : '';
+      } else {  // 'foo' has storage of 0 B
+        testElement.filter = state.filter ? 'foo' : '';
+      }
+
+      testElement.siteGroupMap.get('foo.com')!.hasInstalledPWA =
+          state.appInstalled;
+      testElement.siteGroupMap.get('bar.com')!.hasInstalledPWA =
+          state.appInstalled;
+
+      testElement.forceListUpdateForTesting();
+      await flushTasks();
+
+      const confirmationTitle =
+          confirmClearAllData.querySelector<HTMLElement>(
+                                 '[slot=title]')!.innerText.trim();
+      const confirmationDescription =
+          confirmClearAllData
+              .querySelector<HTMLElement>(
+                  '#clearAllStorageDialogDescription')!.innerText.trim();
+      const confirmationSignOutLabel =
+          confirmClearAllData
+              .querySelector<HTMLElement>(
+                  '#clearAllStorageDialogSignOutLabel')!.innerText.trim();
+
+      assertEquals(loadTimeData.getString(state.title), confirmationTitle);
+      assertEquals(
+          getSubstitutedString(
+              state.description, state.storage ? '100 B' : '0 B'),
+          confirmationDescription);
+      assertEquals(
+          loadTimeData.getString(state.signout), confirmationSignOutLabel);
+    }
+  });
+
   test('can be sorted by storage', async function() {
     setUpAllSites(prefsVarious);
     testElement.currentRouteChanged(routes.SITE_SETTINGS_ALL);
diff --git a/chrome/test/data/webui/settings/privacy_page_test.ts b/chrome/test/data/webui/settings/privacy_page_test.ts
index 838f1ec..0a3cae8a 100644
--- a/chrome/test/data/webui/settings/privacy_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_page_test.ts
@@ -6,9 +6,8 @@
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {ClearBrowsingDataBrowserProxyImpl, ContentSettingsTypes, CookiePrimarySetting, SafeBrowsingSetting, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrLinkRowElement, HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyPageBrowserProxyImpl, Route, Router, routes, SecureDnsMode, SettingsPrivacyPageElement, StatusAction, SyncStatus, TrustSafetyInteraction} from 'chrome://settings/settings.js';
-
+import {ClearBrowsingDataBrowserProxyImpl, ContentSettingsTypes, SiteSettingsPrefsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
+import {CrLinkRowElement, CrSettingsPrefs, HatsBrowserProxyImpl, MetricsBrowserProxyImpl, PrivacyGuideInteractions, PrivacyPageBrowserProxyImpl, Route, Router, routes, SettingsPrefsElement, SettingsPrivacyPageElement, StatusAction, SyncStatus, TrustSafetyInteraction} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 
@@ -62,6 +61,7 @@
 
 suite('PrivacyPage', function() {
   let page: SettingsPrivacyPageElement;
+  let settingsPrefs: SettingsPrefsElement;
   let testClearBrowsingDataBrowserProxy: TestClearBrowsingDataBrowserProxy;
   let siteSettingsBrowserProxy: TestSiteSettingsPrefsBrowserProxy;
   let metricsBrowserProxy: TestMetricsBrowserProxy;
@@ -73,6 +73,9 @@
       isPrivacySandboxRestricted: true,
       privacyGuideEnabled: false,
     });
+
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
   });
 
   setup(function() {
@@ -89,26 +92,7 @@
 
     document.body.innerHTML = '';
     page = document.createElement('settings-privacy-page');
-    page.prefs = {
-      profile: {password_manager_leak_detection: {value: true}},
-      signin: {
-        allowed_on_next_startup:
-            {type: chrome.settingsPrivate.PrefType.BOOLEAN, value: true}
-      },
-      safebrowsing: {
-        enabled: {value: true},
-        scout_reporting_enabled: {value: true},
-        enhanced: {value: false}
-      },
-      dns_over_https:
-          {mode: {value: SecureDnsMode.AUTOMATIC}, templates: {value: ''}},
-      privacy_guide: {
-        viewed: {
-          type: chrome.settingsPrivate.PrefType.BOOLEAN,
-          value: false,
-        },
-      },
-    };
+    page.prefs = settingsPrefs.prefs!;
     document.body.appendChild(page);
     return flushTasks();
   });
@@ -186,6 +170,7 @@
 
 suite('PrivacySandboxEnabled', function() {
   let page: SettingsPrivacyPageElement;
+  let settingsPrefs: SettingsPrefsElement;
   let metricsBrowserProxy: TestMetricsBrowserProxy;
 
   suiteSetup(function() {
@@ -193,6 +178,9 @@
       isPrivacySandboxRestricted: false,
       privacySandboxSettings3Enabled: false,
     });
+
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
   });
 
   setup(function() {
@@ -200,12 +188,7 @@
     MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
     document.body.innerHTML = '';
     page = document.createElement('settings-privacy-page');
-    page.prefs = {
-      privacy_sandbox: {
-        apis_enabled: {value: true},
-        apis_enabled_v2: {value: true},
-      },
-    };
+    page.prefs = settingsPrefs.prefs!;
     document.body.appendChild(page);
     return flushTasks();
   });
@@ -259,36 +242,20 @@
 
 suite('PrivacyGuideEnabled', function() {
   let page: SettingsPrivacyPageElement;
+  let settingsPrefs: SettingsPrefsElement;
   let metricsBrowserProxy: TestMetricsBrowserProxy;
 
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
   setup(function() {
     metricsBrowserProxy = new TestMetricsBrowserProxy();
     MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
     document.body.innerHTML = '';
     page = document.createElement('settings-privacy-page');
-    page.prefs = {
-      // Need privacy_sandbox pref for the page's setup.
-      privacy_sandbox: {
-        apis_enabled: {value: true},
-        apis_enabled_v2: {value: true},
-      },
-      privacy_guide: {
-        viewed: {
-          type: chrome.settingsPrivate.PrefType.BOOLEAN,
-          value: false,
-        },
-      },
-      generated: {
-        cookie_primary_setting: {
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: CookiePrimarySetting.BLOCK_THIRD_PARTY,
-        },
-        safe_browsing: {
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: SafeBrowsingSetting.STANDARD,
-        },
-      },
-    };
+    page.prefs = settingsPrefs.prefs!;
     document.body.appendChild(page);
     return flushTasks();
   });
@@ -348,12 +315,16 @@
 // TODO(1215630): Remove once #privacy-guide-2 has been rolled out.
 suite('PrivacyGuide2Disabled', function() {
   let page: SettingsPrivacyPageElement;
+  let settingsPrefs: SettingsPrefsElement;
   let metricsBrowserProxy: TestMetricsBrowserProxy;
 
   suiteSetup(function() {
     loadTimeData.overrideValues({
       privacyGuide2Enabled: false,
     });
+
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
   });
 
   setup(function() {
@@ -361,29 +332,7 @@
     MetricsBrowserProxyImpl.setInstance(metricsBrowserProxy);
     document.body.innerHTML = '';
     page = document.createElement('settings-privacy-page');
-    page.prefs = {
-      // Need privacy_sandbox pref for the page's setup.
-      privacy_sandbox: {
-        apis_enabled: {value: true},
-        apis_enabled_v2: {value: true},
-      },
-      privacy_guide: {
-        viewed: {
-          type: chrome.settingsPrivate.PrefType.BOOLEAN,
-          value: false,
-        },
-      },
-      generated: {
-        cookie_primary_setting: {
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: CookiePrimarySetting.BLOCK_THIRD_PARTY,
-        },
-        safe_browsing: {
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: SafeBrowsingSetting.STANDARD,
-        },
-      },
-    };
+    page.prefs = settingsPrefs.prefs!;
     document.body.appendChild(page);
     return flushTasks();
   });
@@ -507,31 +456,20 @@
 
 suite('HappinessTrackingSurveys', function() {
   let testHatsBrowserProxy: TestHatsBrowserProxy;
+  let settingsPrefs: SettingsPrefsElement;
   let page: SettingsPrivacyPageElement;
 
+  suiteSetup(function() {
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
   setup(function() {
     testHatsBrowserProxy = new TestHatsBrowserProxy();
     HatsBrowserProxyImpl.setInstance(testHatsBrowserProxy);
     document.body.innerHTML = '';
     page = document.createElement('settings-privacy-page');
-    // Initialize the privacy page pref. Security page manually expands
-    // the initially selected safe browsing option so the pref object
-    // needs to be defined.
-    page.prefs = {
-      generated: {
-        safe_browsing: {
-          type: chrome.settingsPrivate.PrefType.NUMBER,
-          value: SafeBrowsingSetting.STANDARD,
-        },
-        cookie_session_only: {value: false},
-        cookie_primary_setting:
-            {type: chrome.settingsPrivate.PrefType.NUMBER, value: 0},
-        password_manager_leak_detection: {value: false},
-      },
-      profile: {password_manager_leak_detection: {value: false}},
-      dns_over_https:
-          {mode: {value: SecureDnsMode.AUTOMATIC}, templates: {value: ''}},
-    };
+    page.prefs = settingsPrefs.prefs!;
     document.body.appendChild(page);
     return flushTasks();
   });
diff --git a/chromecast/browser/metrics/cast_browser_metrics.cc b/chromecast/browser/metrics/cast_browser_metrics.cc
index 445601f..e20df37 100644
--- a/chromecast/browser/metrics/cast_browser_metrics.cc
+++ b/chromecast/browser/metrics/cast_browser_metrics.cc
@@ -101,7 +101,7 @@
 void CastBrowserMetrics::Finalize() {
 #if !BUILDFLAG(IS_ANDROID)
   // Signal that the session has exited cleanly.
-  metrics_service_client_->GetMetricsService()->RecordCompletedSessionEnd();
+  metrics_service_client_->GetMetricsService()->LogCleanShutdown();
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/chromecast/metrics/cast_metrics_service_client.cc b/chromecast/metrics/cast_metrics_service_client.cc
index ab78dce..89698e3 100644
--- a/chromecast/metrics/cast_metrics_service_client.cc
+++ b/chromecast/metrics/cast_metrics_service_client.cc
@@ -357,7 +357,7 @@
   metrics_service_->InitializeMetricsRecordingState();
 #if !BUILDFLAG(IS_ANDROID)
   // Signal that the session has not yet exited cleanly. We later signal that
-  // the session exited cleanly via MetricsService::RecordCompletedSessionEnd().
+  // the session exited cleanly via MetricsService::LogCleanShutdown().
   // TODO(crbug.com/1208587): See whether this can be called even earlier.
   metrics_state_manager_->LogHasSessionShutdownCleanly(false);
 #endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chromeos/crosapi/mojom/device_settings_service.mojom b/chromeos/crosapi/mojom/device_settings_service.mojom
index 21fbacd..0573a51 100644
--- a/chromeos/crosapi/mojom/device_settings_service.mojom
+++ b/chromeos/crosapi/mojom/device_settings_service.mojom
@@ -24,6 +24,8 @@
 };
 
 // All the device settings data that are needed in Lacros should be here.
+//
+// Next MinVersion: 3
 [Stable]
 struct DeviceSettings {
   // The value of AttestationForContentProtectionEnabled device setting.
@@ -38,6 +40,10 @@
   // The value of DeviceEphemeralUsersEnabled device policy.
   [MinVersion=1] OptionalBool device_ephemeral_users_enabled@3;
 
+  // The value of DeviceRestrictedManagedGuestSessionEnabled device policy.
+  [MinVersion=2]
+  OptionalBool device_restricted_managed_guest_session_enabled@4;
+
   [Stable]
   enum OptionalBool {
     kUnset,
diff --git a/chromeos/language/language_packs/BUILD.gn b/chromeos/language/language_packs/BUILD.gn
index e2ba3e4..8879138 100644
--- a/chromeos/language/language_packs/BUILD.gn
+++ b/chromeos/language/language_packs/BUILD.gn
@@ -21,7 +21,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "language_pack_manager_unittest.cc" ]
+  sources = [
+    "language_pack_manager_unittest.cc",
+    "metrics_unittest.cc",
+  ]
   deps = [
     ":language_packs",
     "//base",
@@ -30,4 +33,9 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  data = [
+    # enums.xml is analyzed by LanguagePackMetricsTest, so this dependency
+    # is needed to make commit bots run unit_tests on enums.xml changes.
+    "//tools/metrics/histograms/enums.xml",
+  ]
 }
diff --git a/chromeos/language/language_packs/metrics_unittest.cc b/chromeos/language/language_packs/metrics_unittest.cc
new file mode 100644
index 0000000..0ff4521
--- /dev/null
+++ b/chromeos/language/language_packs/metrics_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright 2021 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/containers/contains.h"
+#include "base/hash/hash.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/metrics/histogram_enum_reader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos::language_packs {
+
+TEST(LanguagePackMetricsTest, CheckLanguageCodes) {
+  const std::vector<std::string> language_codes = {
+      "am", "ar", "be", "bg", "bn", "ca", "cs", "da", "de", "el", "es",
+      "et", "fa", "fi", "fr", "ga", "gu", "hi", "hr", "hu", "hy", "id",
+      "is", "it", "iw", "ja", "ka", "kk", "km", "kn", "ko", "lo", "lt",
+      "lv", "ml", "mn", "mr", "ms", "mt", "my", "ne", "nl", "no", "or",
+      "pa", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "ta",
+      "te", "th", "ti", "tl", "tr", "uk", "ur", "vi", "zh"};
+
+  absl::optional<base::HistogramEnumEntryMap> language_codes_map =
+      base::ReadEnumFromEnumsXml("LanguagePackLanguageCodes");
+  ASSERT_TRUE(language_codes_map)
+      << "Error reading enum 'LanguagePackLanguageCodes' from "
+         "tools/metrics/histograms/enums.xml.";
+
+  // We prepare the already formatted output in case any code is missing.
+  std::string missing_codes;
+  for (const std::string& code : language_codes) {
+    const auto hashed = static_cast<int32_t>(base::PersistentHash(code));
+    if (!base::Contains(*language_codes_map, hashed)) {
+      base::StrAppend(&missing_codes,
+                      {"<int value=\"", base::NumberToString(hashed),
+                       "\" label=\"", code, "\"/>\n"});
+    }
+  }
+
+  EXPECT_TRUE(missing_codes.empty())
+      << "tools/metrics/histograms/enums.xml enum LanguagePackLanguagesCode "
+         "doesn't contain all expected language codes. "
+      << "Consider adding the following entries:\n\n"
+      << missing_codes;
+}
+
+}  // namespace chromeos::language_packs
diff --git a/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc b/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
index 9d02ddcf..70f2d05a 100644
--- a/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
+++ b/chromeos/services/machine_learning/public/cpp/fake_service_connection.cc
@@ -164,6 +164,7 @@
 
 void FakeServiceConnectionImpl::LoadDocumentScanner(
     mojo::PendingReceiver<mojom::DocumentScanner> receiver,
+    mojom::DocumentScannerConfigPtr config,
     mojom::MachineLearningService::LoadDocumentScannerCallback callback) {
   ScheduleCall(base::BindOnce(
       &FakeServiceConnectionImpl::HandleLoadDocumentScannerCall,
diff --git a/chromeos/services/machine_learning/public/cpp/fake_service_connection.h b/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
index 470063c..fc9e95d6 100644
--- a/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
+++ b/chromeos/services/machine_learning/public/cpp/fake_service_connection.h
@@ -125,6 +125,7 @@
 
   void LoadDocumentScanner(
       mojo::PendingReceiver<mojom::DocumentScanner> receiver,
+      mojom::DocumentScannerConfigPtr config,
       mojom::MachineLearningService::LoadDocumentScannerCallback callback)
       override;
 
diff --git a/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc b/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
index 839ace1..0f600a8 100644
--- a/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/machine_learning/public/cpp/service_connection_unittest.cc
@@ -751,10 +751,11 @@
   ServiceConnection::GetInstance()->Initialize();
 
   std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
+  auto config = chromeos::machine_learning::mojom::DocumentScannerConfig::New();
   ServiceConnection::GetInstance()
       ->GetMachineLearningService()
       .LoadDocumentScanner(
-          scanner.BindNewPipeAndPassReceiver(),
+          scanner.BindNewPipeAndPassReceiver(), std::move(config),
           base::BindOnce(
               [](bool* callback_done, mojom::LoadModelResult result) {
                 EXPECT_EQ(result, mojom::LoadModelResult::OK);
diff --git a/chromeos/services/machine_learning/public/mojom/document_scanner.mojom b/chromeos/services/machine_learning/public/mojom/document_scanner.mojom
index 90d33ccf..7a99a3f 100644
--- a/chromeos/services/machine_learning/public/mojom/document_scanner.mojom
+++ b/chromeos/services/machine_learning/public/mojom/document_scanner.mojom
@@ -19,6 +19,7 @@
 module chromeos.machine_learning.mojom;
 
 import "chromeos/services/machine_learning/public/mojom/document_scanner_param_types.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "mojo/public/mojom/base/shared_memory.mojom";
 
@@ -29,6 +30,16 @@
   ERROR = 1,
 };
 
+// The configuration for document scanner.
+// Next min field ID: 2
+[Stable]
+struct DocumentScannerConfig {
+  string deprecated_library_dlc_path@0;
+
+  // Path to already-installed DocumentScanner library.
+  [MinVersion=1] mojo_base.mojom.FilePath? library_dlc_path@1;
+};
+
 // The corner detection response.
 // Next min field ID: 2
 [Stable]
diff --git a/chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom b/chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom
index 890460b..35e40c3 100644
--- a/chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom
+++ b/chromeos/services/machine_learning/public/mojom/machine_learning_service.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.
 
-// Next MinVersion: 6
+// Next MinVersion: 7
 
 // Top-level API of the Machine Learning Service: loading models for inference.
 
@@ -94,7 +94,8 @@
       => (LoadHandwritingModelResult result);
   // Create and initialize a document scanner.
   [MinVersion=4] LoadDocumentScanner@10(
-            pending_receiver<DocumentScanner> receiver)
+            pending_receiver<DocumentScanner> receiver,
+            [MinVersion=6] DocumentScannerConfig? config)
       => (LoadModelResult result);
   // Creates a web platform model.
   // This function creates a model loader for Web platform flatbuffer models.
diff --git a/chromeos/services/machine_learning/public/mojom/roll_mojoms.sh b/chromeos/services/machine_learning/public/mojom/roll_mojoms.sh
index 17a09b0b..438d0c8 100755
--- a/chromeos/services/machine_learning/public/mojom/roll_mojoms.sh
+++ b/chromeos/services/machine_learning/public/mojom/roll_mojoms.sh
@@ -47,6 +47,9 @@
 echo "Copying mojoms from Chrome OS side ..."
 cp $1/platform2/ml/mojom/*.mojom . || exit 1
 
+echo "Removing file_path.mojom ..."
+rm file_path.mojom || exit 1
+
 echo "Removing time.mojom ..."
 rm time.mojom || exit 1
 
@@ -58,6 +61,7 @@
 
 echo "Changing import paths ..."
 sed --in-place --regexp-extended \
+  -e 's~^import "ml/mojom/file_path.mojom~import "mojo/public/mojom/base/file_path.mojom~g' \
   -e 's~^import "ml/mojom/geometry.mojom~import "ui/gfx/geometry/mojom/geometry.mojom~g' \
   -e 's~^import "ml/mojom/shared_memory.mojom~import "mojo/public/mojom/base/shared_memory.mojom~g' \
   -e 's~^import "ml/mojom/time.mojom~import "mojo/public/mojom/base/time.mojom~g' \
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index f6affbd..3a30605 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -297,7 +297,7 @@
   "crostini.SSHFSMount.bullseye_stable",
   "crostini.SecureCopyPaste.copy_x11_bullseye_stable",
 
-  # http://b234699971
+  # http://b/234699971
   "crostini.AppGeditUnshareFolder",
 
   # b/235279574
@@ -316,6 +316,14 @@
   "crostini.AppAndroidStudio.stable",
   "crostini.AppGeditUnshareFolder.stable",
 
+  # http://crbug.com/1339132
+  "crostini.AppEclipse",
+  "crostini.AppEmacs",
+  "crostini.AppVscode",
+  "crostini.BasicLxdNext.stable",
+  "crostini.RestartApp",
+  "crostini.RestartApp.stable",
+
   # http://crbug.com/1338038
   "arc.IMEBlockingVK",
 
diff --git a/components/autofill/core/browser/autofill_data_util_unittest.cc b/components/autofill/core/browser/autofill_data_util_unittest.cc
index f8f2f399..c4942ee 100644
--- a/components/autofill/core/browser/autofill_data_util_unittest.cc
+++ b/components/autofill/core/browser/autofill_data_util_unittest.cc
@@ -40,16 +40,6 @@
   EXPECT_EQ(expected_group_bitmask, group_bitmask);
 }
 
-TEST(AutofillDataUtilTest, DetermineGroupsForBillingNameAndAddress) {
-  const std::vector<ServerFieldType> field_types{
-      NAME_BILLING_FULL, ADDRESS_BILLING_LINE1, ADDRESS_BILLING_CITY,
-      ADDRESS_BILLING_STATE, ADDRESS_BILLING_ZIP};
-
-  const uint32_t expected_group_bitmask = kName | kAddress;
-  const uint32_t group_bitmask = data_util::DetermineGroups(field_types);
-  EXPECT_EQ(expected_group_bitmask, group_bitmask);
-}
-
 TEST(AutofillDataUtilTest, DetermineGroupsForHomeNamePhoneAndEmail) {
   const std::vector<ServerFieldType> field_types{
       NAME_FULL, PHONE_HOME_CITY_AND_NUMBER, EMAIL_ADDRESS};
@@ -59,15 +49,6 @@
   EXPECT_EQ(expected_group_bitmask, group_bitmask);
 }
 
-TEST(AutofillDataUtilTest, DetermineGroupsForBillingNamePhoneAndEmail) {
-  const std::vector<ServerFieldType> field_types{
-      NAME_BILLING_FULL, PHONE_BILLING_WHOLE_NUMBER, EMAIL_ADDRESS};
-
-  const uint32_t expected_group_bitmask = kName | kPhone | kEmail;
-  const uint32_t group_bitmask = data_util::DetermineGroups(field_types);
-  EXPECT_EQ(expected_group_bitmask, group_bitmask);
-}
-
 TEST(AutofillDataUtilTest, DetermineGroupsForUnknownServerFieldType) {
   const std::vector<ServerFieldType> field_types{UNKNOWN_TYPE, NAME_FULL,
                                                  ADDRESS_HOME_ZIP};
diff --git a/components/autofill/core/browser/autofill_type.cc b/components/autofill/core/browser/autofill_type.cc
index 5237268..509e5ed 100644
--- a/components/autofill/core/browser/autofill_type.cc
+++ b/components/autofill/core/browser/autofill_type.cc
@@ -24,14 +24,6 @@
     case NAME_FULL_WITH_HONORIFIC_PREFIX:
       return FieldTypeGroup::kName;
 
-    case NAME_BILLING_FIRST:
-    case NAME_BILLING_MIDDLE:
-    case NAME_BILLING_LAST:
-    case NAME_BILLING_MIDDLE_INITIAL:
-    case NAME_BILLING_FULL:
-    case NAME_BILLING_SUFFIX:
-      return FieldTypeGroup::kNameBilling;
-
     case EMAIL_ADDRESS:
     case USERNAME_AND_EMAIL_ADDRESS:
       return FieldTypeGroup::kEmail;
@@ -48,13 +40,6 @@
     case PHONE_HOME_EXTENSION:
       return FieldTypeGroup::kPhoneHome;
 
-    case PHONE_BILLING_NUMBER:
-    case PHONE_BILLING_CITY_CODE:
-    case PHONE_BILLING_COUNTRY_CODE:
-    case PHONE_BILLING_CITY_AND_NUMBER:
-    case PHONE_BILLING_WHOLE_NUMBER:
-      return FieldTypeGroup::kPhoneBilling;
-
     case ADDRESS_HOME_LINE1:
     case ADDRESS_HOME_LINE2:
     case ADDRESS_HOME_LINE3:
@@ -78,19 +63,6 @@
     case ADDRESS_HOME_FLOOR:
       return FieldTypeGroup::kAddressHome;
 
-    case ADDRESS_BILLING_LINE1:
-    case ADDRESS_BILLING_LINE2:
-    case ADDRESS_BILLING_LINE3:
-    case ADDRESS_BILLING_APT_NUM:
-    case ADDRESS_BILLING_CITY:
-    case ADDRESS_BILLING_STATE:
-    case ADDRESS_BILLING_ZIP:
-    case ADDRESS_BILLING_COUNTRY:
-    case ADDRESS_BILLING_STREET_ADDRESS:
-    case ADDRESS_BILLING_SORTING_CODE:
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
-      return FieldTypeGroup::kAddressBilling;
-
     case CREDIT_CARD_NAME_FULL:
     case CREDIT_CARD_NAME_FIRST:
     case CREDIT_CARD_NAME_LAST:
@@ -260,80 +232,8 @@
 }
 
 ServerFieldType AutofillType::GetStorableType() const {
-  // Map billing types to the equivalent non-billing types.
-  switch (server_type_) {
-    case ADDRESS_BILLING_LINE1:
-      return ADDRESS_HOME_LINE1;
-
-    case ADDRESS_BILLING_LINE2:
-      return ADDRESS_HOME_LINE2;
-
-    case ADDRESS_BILLING_LINE3:
-      return ADDRESS_HOME_LINE3;
-
-    case ADDRESS_BILLING_APT_NUM:
-      return ADDRESS_HOME_APT_NUM;
-
-    case ADDRESS_BILLING_CITY:
-      return ADDRESS_HOME_CITY;
-
-    case ADDRESS_BILLING_STATE:
-      return ADDRESS_HOME_STATE;
-
-    case ADDRESS_BILLING_ZIP:
-      return ADDRESS_HOME_ZIP;
-
-    case ADDRESS_BILLING_COUNTRY:
-      return ADDRESS_HOME_COUNTRY;
-
-    case PHONE_BILLING_WHOLE_NUMBER:
-      return PHONE_HOME_WHOLE_NUMBER;
-
-    case PHONE_BILLING_NUMBER:
-      return PHONE_HOME_NUMBER;
-
-    case PHONE_BILLING_CITY_CODE:
-      return PHONE_HOME_CITY_CODE;
-
-    case PHONE_BILLING_COUNTRY_CODE:
-      return PHONE_HOME_COUNTRY_CODE;
-
-    case PHONE_BILLING_CITY_AND_NUMBER:
-      return PHONE_HOME_CITY_AND_NUMBER;
-
-    case NAME_BILLING_FIRST:
-      return NAME_FIRST;
-
-    case NAME_BILLING_MIDDLE:
-      return NAME_MIDDLE;
-
-    case NAME_BILLING_LAST:
-      return NAME_LAST;
-
-    case NAME_BILLING_MIDDLE_INITIAL:
-      return NAME_MIDDLE_INITIAL;
-
-    case NAME_BILLING_FULL:
-      return NAME_FULL;
-
-    case NAME_BILLING_SUFFIX:
-      return NAME_SUFFIX;
-
-    case ADDRESS_BILLING_STREET_ADDRESS:
-      return ADDRESS_HOME_STREET_ADDRESS;
-
-    case ADDRESS_BILLING_SORTING_CODE:
-      return ADDRESS_HOME_SORTING_CODE;
-
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
-      return ADDRESS_HOME_DEPENDENT_LOCALITY;
-
-    case UNKNOWN_TYPE:
-      break;  // Try to parse HTML types instead.
-
-    default:
-      return server_type_;
-  }
+  if (server_type_ != UNKNOWN_TYPE)
+    return server_type_;
 
   switch (html_type_) {
     case HTML_TYPE_UNSPECIFIED:
diff --git a/components/autofill/core/browser/autofill_type_unittest.cc b/components/autofill/core/browser/autofill_type_unittest.cc
index 9c4a5bb..ef7b9e64 100644
--- a/components/autofill/core/browser/autofill_type_unittest.cc
+++ b/components/autofill/core/browser/autofill_type_unittest.cc
@@ -29,16 +29,6 @@
   EXPECT_EQ(PHONE_HOME_NUMBER, phone.GetStorableType());
   EXPECT_EQ(FieldTypeGroup::kPhoneHome, phone.group());
 
-  // Billing type.
-  AutofillType billing_address(ADDRESS_BILLING_LINE1);
-  EXPECT_EQ(ADDRESS_HOME_LINE1, billing_address.GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kAddressBilling, billing_address.group());
-
-  // Last value, to check any offset errors.
-  AutofillType last(NAME_BILLING_SUFFIX);
-  EXPECT_EQ(NAME_SUFFIX, last.GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kNameBilling, last.group());
-
   // Boundary (error) condition.
   AutofillType boundary(MAX_VALID_FIELD_TYPE);
   EXPECT_EQ(UNKNOWN_TYPE, boundary.GetStorableType());
diff --git a/components/autofill/core/browser/data_model/autofill_profile_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
index 7474aff..fbfb1f4 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_unittest.cc
@@ -615,7 +615,6 @@
   // should not fall back to the full name as a distinguishing field.
   std::vector<ServerFieldType> suggested_fields;
   suggested_fields.push_back(ADDRESS_HOME_LINE1);
-  suggested_fields.push_back(ADDRESS_BILLING_LINE1);
   suggested_fields.push_back(EMAIL_ADDRESS);
   std::vector<std::u16string> labels;
   AutofillProfile::CreateInferredLabels(ToRawPointerVector(profiles),
diff --git a/components/autofill/core/browser/data_model/phone_number_unittest.cc b/components/autofill/core/browser/data_model/phone_number_unittest.cc
index 52bd3f6..aab04e5 100644
--- a/components/autofill/core/browser/data_model/phone_number_unittest.cc
+++ b/components/autofill/core/browser/data_model/phone_number_unittest.cc
@@ -241,7 +241,7 @@
   profile.SetRawInfo(ADDRESS_HOME_COUNTRY, u"US");
 
   PhoneNumber::PhoneCombineHelper number1;
-  EXPECT_FALSE(number1.SetInfo(AutofillType(ADDRESS_BILLING_CITY), u"1"));
+  EXPECT_FALSE(number1.SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), u"1"));
   EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE), u"1"));
   EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_CITY_CODE), u"650"));
   EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_NUMBER), u"2345678"));
diff --git a/components/autofill/core/browser/field_filler_unittest.cc b/components/autofill/core/browser/field_filler_unittest.cc
index 1d14272..dbc5d521 100644
--- a/components/autofill/core/browser/field_filler_unittest.cc
+++ b/components/autofill/core/browser/field_filler_unittest.cc
@@ -167,21 +167,21 @@
   EXPECT_EQ(FieldTypeGroup::kName, field.Type().group());
 
   // Set the server type and check it.
-  prediction.set_type(ADDRESS_BILLING_LINE1);
+  prediction.set_type(ADDRESS_HOME_LINE1);
   field.set_server_predictions({prediction});
   EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group());
+  EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group());
 
   // Checks that overall_type trumps everything.
-  field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP));
+  field.SetTypeTo(AutofillType(ADDRESS_HOME_ZIP));
   EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group());
+  EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group());
 
   // Checks that setting server type resets overall type.
-  prediction.set_type(ADDRESS_BILLING_LINE1);
+  prediction.set_type(ADDRESS_HOME_LINE1);
   field.set_server_predictions({prediction});
   EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group());
+  EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group());
 
   // Remove the server type to make sure the heuristic type is preserved.
   prediction.set_type(NO_SERVER_DATA);
@@ -190,9 +190,9 @@
   EXPECT_EQ(FieldTypeGroup::kName, field.Type().group());
 
   // Checks that overall_type trumps everything.
-  field.SetTypeTo(AutofillType(ADDRESS_BILLING_ZIP));
+  field.SetTypeTo(AutofillType(ADDRESS_HOME_ZIP));
   EXPECT_EQ(ADDRESS_HOME_ZIP, field.Type().GetStorableType());
-  EXPECT_EQ(FieldTypeGroup::kAddressBilling, field.Type().group());
+  EXPECT_EQ(FieldTypeGroup::kAddressHome, field.Type().group());
 
   // Set the heuristic type and check it and reset overall Type.
   field.set_heuristic_type(GetActivePatternSource(), NAME_FIRST);
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc
index 15d1af44..375e19a 100644
--- a/components/autofill/core/browser/field_types.cc
+++ b/components/autofill/core/browser/field_types.cc
@@ -23,10 +23,16 @@
            // Shipping addresses (values [44,50]) are deprecated.
            !(44 <= t && t <= 50) &&
            // Probably-account creation password (value 94) is deprecated.
-           !(t == 94) &&
+           t != 94 &&
+           // Billing addresses (values [37,43], 78, 80, 82, 84) are deprecated.
+           !(37 <= t && t <= 43) && t != 78 && t != 80 && t != 82 && t != 84 &&
+           // Billing phone numbers (values [62,66]) are deprecated.
+           !(62 <= t && t <= 66) &&
+           // Billing names (values [67, 72]) are deprecated.
+           !(67 <= t && t <= 72) &&
            // Fax numbers (values [20,24]) are deprecated in Chrome, but still
            // supported by the server.
-           !(t >= PHONE_FAX_NUMBER && t <= PHONE_FAX_WHOLE_NUMBER);
+           !(PHONE_FAX_NUMBER <= t && t <= PHONE_FAX_WHOLE_NUMBER);
   };
   return IsValid(raw_value) ? static_cast<ServerFieldType>(raw_value)
                             : fallback_value;
@@ -80,32 +86,6 @@
     case ADDRESS_HOME_FLOOR:
       return true;
 
-    // Billing address types that should not be returned by GetStorableType().
-    case NAME_BILLING_FIRST:
-    case NAME_BILLING_MIDDLE:
-    case NAME_BILLING_LAST:
-    case NAME_BILLING_MIDDLE_INITIAL:
-    case NAME_BILLING_FULL:
-    case NAME_BILLING_SUFFIX:
-    case PHONE_BILLING_NUMBER:
-    case PHONE_BILLING_CITY_CODE:
-    case PHONE_BILLING_COUNTRY_CODE:
-    case PHONE_BILLING_CITY_AND_NUMBER:
-    case PHONE_BILLING_WHOLE_NUMBER:
-    case ADDRESS_BILLING_LINE1:
-    case ADDRESS_BILLING_LINE2:
-    case ADDRESS_BILLING_LINE3:
-    case ADDRESS_BILLING_APT_NUM:
-    case ADDRESS_BILLING_CITY:
-    case ADDRESS_BILLING_STATE:
-    case ADDRESS_BILLING_ZIP:
-    case ADDRESS_BILLING_COUNTRY:
-    case ADDRESS_BILLING_STREET_ADDRESS:
-    case ADDRESS_BILLING_SORTING_CODE:
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
-      NOTREACHED();
-      return false;
-
     case CREDIT_CARD_NAME_FULL:
     case CREDIT_CARD_NAME_FIRST:
     case CREDIT_CARD_NAME_LAST:
@@ -205,18 +185,6 @@
       return "NAME_FULL";
     case NAME_SUFFIX:
       return "NAME_SUFFIX";
-    case NAME_BILLING_FIRST:
-      return "NAME_BILLING_FIRST";
-    case NAME_BILLING_MIDDLE:
-      return "NAME_BILLING_MIDDLE";
-    case NAME_BILLING_LAST:
-      return "NAME_BILLING_LAST";
-    case NAME_BILLING_MIDDLE_INITIAL:
-      return "NAME_BILLING_MIDDLE_INITIAL";
-    case NAME_BILLING_FULL:
-      return "NAME_BILLING_FULL";
-    case NAME_BILLING_SUFFIX:
-      return "NAME_BILLING_SUFFIX";
     case EMAIL_ADDRESS:
       return "EMAIL_ADDRESS";
     case PHONE_HOME_NUMBER:
@@ -271,22 +239,6 @@
       return "ADDRESS_HOME_ZIP";
     case ADDRESS_HOME_COUNTRY:
       return "ADDRESS_HOME_COUNTRY";
-    case ADDRESS_BILLING_LINE1:
-      return "ADDRESS_BILLING_LINE1";
-    case ADDRESS_BILLING_LINE2:
-      return "ADDRESS_BILLING_LINE2";
-    case ADDRESS_BILLING_LINE3:
-      return "ADDRESS_BILLING_LINE3";
-    case ADDRESS_BILLING_APT_NUM:
-      return "ADDRESS_BILLING_APT_NUM";
-    case ADDRESS_BILLING_CITY:
-      return "ADDRESS_BILLING_CITY";
-    case ADDRESS_BILLING_STATE:
-      return "ADDRESS_BILLING_STATE";
-    case ADDRESS_BILLING_ZIP:
-      return "ADDRESS_BILLING_ZIP";
-    case ADDRESS_BILLING_COUNTRY:
-      return "ADDRESS_BILLING_COUNTRY";
     case BIRTHDATE_DAY:
       return "BIRTHDATE_DAY";
     case BIRTHDATE_MONTH:
@@ -319,16 +271,6 @@
       return "COMPANY_NAME";
     case FIELD_WITH_DEFAULT_VALUE:
       return "FIELD_WITH_DEFAULT_VALUE";
-    case PHONE_BILLING_NUMBER:
-      return "PHONE_BILLING_NUMBER";
-    case PHONE_BILLING_CITY_CODE:
-      return "PHONE_BILLING_CITY_CODE";
-    case PHONE_BILLING_COUNTRY_CODE:
-      return "PHONE_BILLING_COUNTRY_CODE";
-    case PHONE_BILLING_CITY_AND_NUMBER:
-      return "PHONE_BILLING_CITY_AND_NUMBER";
-    case PHONE_BILLING_WHOLE_NUMBER:
-      return "PHONE_BILLING_WHOLE_NUMBER";
     case MERCHANT_EMAIL_SIGNUP:
       return "MERCHANT_EMAIL_SIGNUP";
     case MERCHANT_PROMO_CODE:
@@ -339,16 +281,10 @@
       return "ACCOUNT_CREATION_PASSWORD";
     case ADDRESS_HOME_STREET_ADDRESS:
       return "ADDRESS_HOME_STREET_ADDRESS";
-    case ADDRESS_BILLING_STREET_ADDRESS:
-      return "ADDRESS_BILLING_STREET_ADDRESS";
     case ADDRESS_HOME_SORTING_CODE:
       return "ADDRESS_HOME_SORTING_CODE";
-    case ADDRESS_BILLING_SORTING_CODE:
-      return "ADDRESS_BILLING_SORTING_CODE";
     case ADDRESS_HOME_DEPENDENT_LOCALITY:
       return "ADDRESS_HOME_DEPENDENT_LOCALITY";
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
-      return "ADDRESS_BILLING_DEPENDENT_LOCALITY";
     case NOT_ACCOUNT_CREATION_PASSWORD:
       return "NOT_ACCOUNT_CREATION_PASSWORD";
     case USERNAME:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index adc66c8..7d6fe95 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -68,15 +68,9 @@
   ADDRESS_HOME_STATE = 34,
   ADDRESS_HOME_ZIP = 35,
   ADDRESS_HOME_COUNTRY = 36,
-  ADDRESS_BILLING_LINE1 = 37,
-  ADDRESS_BILLING_LINE2 = 38,
-  ADDRESS_BILLING_APT_NUM = 39,
-  ADDRESS_BILLING_CITY = 40,
-  ADDRESS_BILLING_STATE = 41,
-  ADDRESS_BILLING_ZIP = 42,
-  ADDRESS_BILLING_COUNTRY = 43,
 
-  // ADDRESS_SHIPPING values [44,50] are deprecated.
+  // ADDRESS_BILLING values [37, 43] are deprecated.
+  // ADDRESS_SHIPPING values [44, 50] are deprecated.
 
   CREDIT_CARD_NAME_FULL = 51,
   CREDIT_CARD_NUMBER = 52,
@@ -93,18 +87,8 @@
   // Generic type whose default value is known.
   FIELD_WITH_DEFAULT_VALUE = 61,
 
-  PHONE_BILLING_NUMBER = 62,
-  PHONE_BILLING_CITY_CODE = 63,
-  PHONE_BILLING_COUNTRY_CODE = 64,
-  PHONE_BILLING_CITY_AND_NUMBER = 65,
-  PHONE_BILLING_WHOLE_NUMBER = 66,
-
-  NAME_BILLING_FIRST = 67,
-  NAME_BILLING_MIDDLE = 68,
-  NAME_BILLING_LAST = 69,
-  NAME_BILLING_MIDDLE_INITIAL = 70,
-  NAME_BILLING_FULL = 71,
-  NAME_BILLING_SUFFIX = 72,
+  // PHONE_BILLING values [62, 66] are deprecated.
+  // NAME_BILLING values [67, 72] are deprecated.
 
   // Field types for options generally found in merchant buyflows. Given that
   // these are likely to be filled out differently on a case by case basis,
@@ -124,7 +108,7 @@
   //   123 Main Street,
   //   Apt. #42
   ADDRESS_HOME_STREET_ADDRESS = 77,
-  ADDRESS_BILLING_STREET_ADDRESS = 78,
+  // ADDRESS_BILLING_STREET_ADDRESS 78 is deprecated.
 
   // A sorting code is similar to a postal code. However, whereas a postal code
   // normally refers to a single geographical location, a sorting code often
@@ -132,17 +116,17 @@
   // might be geographically distributed. The most prominent example of a
   // sorting code system is CEDEX in France.
   ADDRESS_HOME_SORTING_CODE = 79,
-  ADDRESS_BILLING_SORTING_CODE = 80,
+  // ADDRESS_BILLING_SORTING_CODE 80 is deprecated.
 
   // A dependent locality is a subunit of a locality, where a "locality" is
   // roughly equivalent to a city. Examples of dependent localities include
   // inner-city districts and suburbs.
   ADDRESS_HOME_DEPENDENT_LOCALITY = 81,
-  ADDRESS_BILLING_DEPENDENT_LOCALITY = 82,
+  // ADDRESS_BILLING_DEPENDENT_LOCALITY 82 is deprecated.
 
   // The third line of the street address.
   ADDRESS_HOME_LINE3 = 83,
-  ADDRESS_BILLING_LINE3 = 84,
+  // ADDRESS_BILLING_LINE3 84 is deprecated.
 
   // Inverse of ACCOUNT_CREATION_PASSWORD. Sent when there is data that
   // a previous upload of ACCOUNT_CREATION_PASSWORD was incorrect.
diff --git a/components/autofill/core/browser/field_types_unittest.cc b/components/autofill/core/browser/field_types_unittest.cc
index f4afb70..8e1b63d26 100644
--- a/components/autofill/core/browser/field_types_unittest.cc
+++ b/components/autofill/core/browser/field_types_unittest.cc
@@ -36,13 +36,6 @@
       ADDRESS_HOME_STATE,
       ADDRESS_HOME_ZIP,
       ADDRESS_HOME_COUNTRY,
-      ADDRESS_BILLING_LINE1,
-      ADDRESS_BILLING_LINE2,
-      ADDRESS_BILLING_APT_NUM,
-      ADDRESS_BILLING_CITY,
-      ADDRESS_BILLING_STATE,
-      ADDRESS_BILLING_ZIP,
-      ADDRESS_BILLING_COUNTRY,
       CREDIT_CARD_NAME_FULL,
       CREDIT_CARD_NUMBER,
       CREDIT_CARD_EXP_MONTH,
@@ -54,29 +47,14 @@
       CREDIT_CARD_VERIFICATION_CODE,
       COMPANY_NAME,
       FIELD_WITH_DEFAULT_VALUE,
-      PHONE_BILLING_NUMBER,
-      PHONE_BILLING_CITY_CODE,
-      PHONE_BILLING_COUNTRY_CODE,
-      PHONE_BILLING_CITY_AND_NUMBER,
-      PHONE_BILLING_WHOLE_NUMBER,
-      NAME_BILLING_FIRST,
-      NAME_BILLING_MIDDLE,
-      NAME_BILLING_LAST,
-      NAME_BILLING_MIDDLE_INITIAL,
-      NAME_BILLING_FULL,
-      NAME_BILLING_SUFFIX,
       MERCHANT_EMAIL_SIGNUP,
       MERCHANT_PROMO_CODE,
       PASSWORD,
       ACCOUNT_CREATION_PASSWORD,
       ADDRESS_HOME_STREET_ADDRESS,
-      ADDRESS_BILLING_STREET_ADDRESS,
       ADDRESS_HOME_SORTING_CODE,
-      ADDRESS_BILLING_SORTING_CODE,
       ADDRESS_HOME_DEPENDENT_LOCALITY,
-      ADDRESS_BILLING_DEPENDENT_LOCALITY,
       ADDRESS_HOME_LINE3,
-      ADDRESS_BILLING_LINE3,
       NOT_ACCOUNT_CREATION_PASSWORD,
       USERNAME,
       USERNAME_AND_EMAIL_ADDRESS,
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index c3ba3ec..2093107 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -1720,7 +1720,6 @@
         cc_cvc_found = true;
         break;
       case ADDRESS_HOME_ZIP:
-      case ADDRESS_BILLING_ZIP:
         // Zip/Postal code often appears as part of a Credit Card form. Do
         // not count it as a non-cc-related field.
         break;
@@ -2169,8 +2168,6 @@
   RationalizeAddressLineFields(
       &(sectioned_field_indexes_by_type[ADDRESS_HOME_STREET_ADDRESS]),
       form_interactions_ukm_logger, log_manager);
-  // Since the billing types are mapped to the non-billing ones, no need to
-  // take care of ADDRESS_BILLING_STATE and .. .
   RationalizeAddressStateCountry(
       &(sectioned_field_indexes_by_type[ADDRESS_HOME_STATE]),
       &(sectioned_field_indexes_by_type[ADDRESS_HOME_COUNTRY]),
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 8db5b470..7e4883ed 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -2728,8 +2728,6 @@
   available_field_types.insert(ADDRESS_HOME_LINE1);
   available_field_types.insert(ADDRESS_HOME_LINE2);
   available_field_types.insert(ADDRESS_HOME_COUNTRY);
-  available_field_types.insert(ADDRESS_BILLING_LINE1);
-  available_field_types.insert(ADDRESS_BILLING_LINE2);
   available_field_types.insert(EMAIL_ADDRESS);
   available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
 
@@ -2739,7 +2737,7 @@
   upload.set_client_version(GetProductNameAndVersionForUserAgent());
   upload.set_form_signature(form_structure->form_signature().value());
   upload.set_autofill_used(false);
-  upload.set_data_present("144200030e");
+  upload.set_data_present("1442000308");
   upload.set_passwords_revealed(false);
   upload.set_password_has_lowercase_letter(true);
   upload.set_password_length(10u);
@@ -2784,10 +2782,8 @@
     form.fields.push_back(field);
     test::InitializePossibleTypesAndValidities(
         possible_field_types, possible_field_types_validities,
-        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1,
-         ADDRESS_BILLING_LINE2},
-        {AutofillProfile::VALID, AutofillProfile::VALID,
-         AutofillProfile::INVALID, AutofillProfile::INVALID});
+        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2},
+        {AutofillProfile::VALID, AutofillProfile::VALID});
   }
 
   form_structure = std::make_unique<FormStructure>(form);
@@ -2812,9 +2808,9 @@
   // Create an additional 2 fields (total of 7).  Put the appropriate autofill
   // type on the different address fields.
   test::FillUploadField(upload.add_field(), 509334676U, "address", "text",
-                        nullptr, {30U, 31U, 37U, 38U}, {2, 2, 3, 3});
+                        nullptr, {30U, 31U}, {2, 2});
   test::FillUploadField(upload.add_field(), 509334676U, "address", "text",
-                        nullptr, {30U, 31U, 37U, 38U}, {2, 2, 3, 3});
+                        nullptr, {30U, 31U}, {2, 2});
 
   EXPECT_THAT(form_structure->EncodeUploadRequest(available_field_types, false,
                                                   std::string(), true, true),
@@ -2911,8 +2907,6 @@
   available_field_types.insert(ADDRESS_HOME_LINE1);
   available_field_types.insert(ADDRESS_HOME_LINE2);
   available_field_types.insert(ADDRESS_HOME_COUNTRY);
-  available_field_types.insert(ADDRESS_BILLING_LINE1);
-  available_field_types.insert(ADDRESS_BILLING_LINE2);
   available_field_types.insert(EMAIL_ADDRESS);
   available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
 
@@ -2922,7 +2916,7 @@
   upload.set_client_version(GetProductNameAndVersionForUserAgent());
   upload.set_form_signature(form_structure->form_signature().value());
   upload.set_autofill_used(false);
-  upload.set_data_present("144200030e");
+  upload.set_data_present("1442000308");
   upload.set_passwords_revealed(false);
   upload.set_password_has_lowercase_letter(true);
   upload.set_password_length(10u);
@@ -3037,8 +3031,6 @@
   available_field_types.insert(ADDRESS_HOME_LINE1);
   available_field_types.insert(ADDRESS_HOME_LINE2);
   available_field_types.insert(ADDRESS_HOME_COUNTRY);
-  available_field_types.insert(ADDRESS_BILLING_LINE1);
-  available_field_types.insert(ADDRESS_BILLING_LINE2);
   available_field_types.insert(EMAIL_ADDRESS);
   available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
 
@@ -3048,7 +3040,7 @@
   upload.set_client_version(GetProductNameAndVersionForUserAgent());
   upload.set_form_signature(form_structure->form_signature().value());
   upload.set_autofill_used(false);
-  upload.set_data_present("144200030e");
+  upload.set_data_present("1442000308");
   upload.set_passwords_revealed(false);
   upload.set_password_has_lowercase_letter(true);
   upload.set_password_length(10u);
@@ -3160,8 +3152,6 @@
   available_field_types.insert(ADDRESS_HOME_LINE1);
   available_field_types.insert(ADDRESS_HOME_LINE2);
   available_field_types.insert(ADDRESS_HOME_COUNTRY);
-  available_field_types.insert(ADDRESS_BILLING_LINE1);
-  available_field_types.insert(ADDRESS_BILLING_LINE2);
   available_field_types.insert(EMAIL_ADDRESS);
   available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
 
@@ -3172,7 +3162,7 @@
   upload.set_client_version(GetProductNameAndVersionForUserAgent());
   upload.set_form_signature(form_structure->form_signature().value());
   upload.set_autofill_used(false);
-  upload.set_data_present("144200030e");
+  upload.set_data_present("1442000308");
   upload.set_passwords_revealed(false);
   upload.set_password_has_lowercase_letter(true);
   upload.set_password_length(10u);
@@ -3208,8 +3198,7 @@
     form.fields.push_back(field);
     test::InitializePossibleTypesAndValidities(
         possible_field_types, possible_field_types_validities,
-        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1,
-         ADDRESS_BILLING_LINE2});
+        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2});
   }
 
   form_structure = std::make_unique<FormStructure>(form);
@@ -3242,9 +3231,9 @@
   }
   // Put the appropriate autofill type on the different address fields.
   test::FillUploadField(upload.mutable_field(5), 509334676U, "address", "text",
-                        nullptr, {31U, 37U, 38U});
+                        nullptr, 31U);
   test::FillUploadField(upload.mutable_field(6), 509334676U, "address", "text",
-                        nullptr, {31U, 37U, 38U});
+                        nullptr, 31U);
 
   EXPECT_THAT(form_structure->EncodeUploadRequest(available_field_types, false,
                                                   std::string(), true, true),
@@ -3260,8 +3249,7 @@
     form.fields.push_back(field);
     test::InitializePossibleTypesAndValidities(
         possible_field_types, possible_field_types_validities,
-        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2, ADDRESS_BILLING_LINE1,
-         ADDRESS_BILLING_LINE2});
+        {ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE2});
   }
   form_structure = std::make_unique<FormStructure>(form);
   ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
@@ -7246,13 +7234,13 @@
   AddFieldSuggestionToForm(form_suggestion, form.fields[8], ADDRESS_HOME_STATE);
   AddFieldSuggestionToForm(form_suggestion, form.fields[9], NAME_FULL);
   AddFieldSuggestionToForm(form_suggestion, form.fields[10],
-                           ADDRESS_BILLING_STATE);
+                           ADDRESS_HOME_STATE);
   // third section
   AddFieldSuggestionToForm(form_suggestion, form.fields[11],
-                           ADDRESS_BILLING_STATE);
+                           ADDRESS_HOME_STATE);
   AddFieldSuggestionToForm(form_suggestion, form.fields[12], NAME_FULL);
   AddFieldSuggestionToForm(form_suggestion, form.fields[13],
-                           ADDRESS_BILLING_STATE);
+                           ADDRESS_HOME_STATE);
 
   std::string response_string = SerializeAndEncode(response);
 
@@ -7399,15 +7387,15 @@
                            ADDRESS_HOME_COUNTRY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[7], ADDRESS_HOME_CITY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[8],
-                           ADDRESS_BILLING_COUNTRY);
+                           ADDRESS_HOME_COUNTRY);
   // third section
   AddFieldSuggestionToForm(form_suggestion, form.fields[9], ADDRESS_HOME_CITY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[10],
-                           ADDRESS_BILLING_COUNTRY);
+                           ADDRESS_HOME_COUNTRY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[11],
                            ADDRESS_HOME_COUNTRY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[12],
-                           ADDRESS_BILLING_COUNTRY);
+                           ADDRESS_HOME_COUNTRY);
   AddFieldSuggestionToForm(form_suggestion, form.fields[13],
                            ADDRESS_HOME_COUNTRY);
 
@@ -7487,8 +7475,7 @@
   AddFieldSuggestionToForm(form_suggestion, form.fields[1], ADDRESS_HOME_STATE);
   AddFieldSuggestionToForm(form_suggestion, form.fields[2], ADDRESS_HOME_STATE);
   AddFieldSuggestionToForm(form_suggestion, form.fields[3], NAME_FULL);
-  AddFieldSuggestionToForm(form_suggestion, form.fields[4],
-                           ADDRESS_BILLING_STATE);
+  AddFieldSuggestionToForm(form_suggestion, form.fields[4], ADDRESS_HOME_STATE);
 
   std::string response_string = SerializeAndEncode(response);
 
@@ -8243,8 +8230,8 @@
   form_structure.set_overall_field_type_for_testing(1, PHONE_HOME_NUMBER);
   form_structure.set_overall_field_type_for_testing(2, PHONE_HOME_NUMBER);
   form_structure.set_overall_field_type_for_testing(3, NAME_FULL);
-  form_structure.set_overall_field_type_for_testing(4, PHONE_BILLING_NUMBER);
-  form_structure.set_overall_field_type_for_testing(5, PHONE_BILLING_NUMBER);
+  form_structure.set_overall_field_type_for_testing(4, PHONE_HOME_NUMBER);
+  form_structure.set_overall_field_type_for_testing(5, PHONE_HOME_NUMBER);
   form_structure.set_overall_field_type_for_testing(6, ADDRESS_HOME_COUNTRY);
 
   std::vector<FormStructure*> forms;
diff --git a/components/autofill/core/browser/geo/address_i18n.cc b/components/autofill/core/browser/geo/address_i18n.cc
index fb38157..1eeef91e 100644
--- a/components/autofill/core/browser/geo/address_i18n.cc
+++ b/components/autofill/core/browser/geo/address_i18n.cc
@@ -94,39 +94,30 @@
 
 bool FieldForType(ServerFieldType server_type, AddressField* field) {
   switch (server_type) {
-    case ADDRESS_BILLING_COUNTRY:
     case ADDRESS_HOME_COUNTRY:
       if (field)
         *field = ::i18n::addressinput::COUNTRY;
       return true;
-    case ADDRESS_BILLING_STATE:
     case ADDRESS_HOME_STATE:
       if (field)
         *field = ::i18n::addressinput::ADMIN_AREA;
       return true;
-    case ADDRESS_BILLING_CITY:
     case ADDRESS_HOME_CITY:
       if (field)
         *field = ::i18n::addressinput::LOCALITY;
       return true;
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
     case ADDRESS_HOME_DEPENDENT_LOCALITY:
       if (field)
         *field = ::i18n::addressinput::DEPENDENT_LOCALITY;
       return true;
-    case ADDRESS_BILLING_SORTING_CODE:
     case ADDRESS_HOME_SORTING_CODE:
       if (field)
         *field = ::i18n::addressinput::SORTING_CODE;
       return true;
-    case ADDRESS_BILLING_ZIP:
     case ADDRESS_HOME_ZIP:
       if (field)
         *field = ::i18n::addressinput::POSTAL_CODE;
       return true;
-    case ADDRESS_BILLING_STREET_ADDRESS:
-    case ADDRESS_BILLING_LINE1:
-    case ADDRESS_BILLING_LINE2:
     case ADDRESS_HOME_STREET_ADDRESS:
     case ADDRESS_HOME_LINE1:
     case ADDRESS_HOME_LINE2:
@@ -137,7 +128,6 @@
       if (field)
         *field = ::i18n::addressinput::ORGANIZATION;
       return true;
-    case NAME_BILLING_FULL:
     case NAME_FULL:
       if (field)
         *field = ::i18n::addressinput::RECIPIENT;
diff --git a/components/autofill/core/browser/geo/address_i18n_unittest.cc b/components/autofill/core/browser/geo/address_i18n_unittest.cc
index bd9f8160..7268c8b 100644
--- a/components/autofill/core/browser/geo/address_i18n_unittest.cc
+++ b/components/autofill/core/browser/geo/address_i18n_unittest.cc
@@ -90,10 +90,6 @@
                          FieldTypeUnidirectionalConversionsTest,
                          testing::Values(
                              FieldTypeUnidirectionalConversionsTestCase{
-                                 ADDRESS_BILLING_LINE1, STREET_ADDRESS},
-                             FieldTypeUnidirectionalConversionsTestCase{
-                                 ADDRESS_BILLING_LINE2, STREET_ADDRESS},
-                             FieldTypeUnidirectionalConversionsTestCase{
                                  ADDRESS_HOME_LINE1, STREET_ADDRESS},
                              FieldTypeUnidirectionalConversionsTestCase{
                                  ADDRESS_HOME_LINE2, STREET_ADDRESS}));
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 58b463a..b782bac 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -340,13 +340,6 @@
         case PHONE_FAX_COUNTRY_CODE:
         case PHONE_FAX_CITY_AND_NUMBER:
         case PHONE_FAX_WHOLE_NUMBER:
-        case ADDRESS_BILLING_LINE1:
-        case ADDRESS_BILLING_LINE2:
-        case ADDRESS_BILLING_APT_NUM:
-        case ADDRESS_BILLING_CITY:
-        case ADDRESS_BILLING_STATE:
-        case ADDRESS_BILLING_ZIP:
-        case ADDRESS_BILLING_COUNTRY:
         case CREDIT_CARD_NAME_FULL:
         case CREDIT_CARD_NUMBER:
         case CREDIT_CARD_EXP_MONTH:
@@ -358,25 +351,10 @@
         case CREDIT_CARD_VERIFICATION_CODE:
         case COMPANY_NAME:
         case FIELD_WITH_DEFAULT_VALUE:
-        case PHONE_BILLING_NUMBER:
-        case PHONE_BILLING_CITY_CODE:
-        case PHONE_BILLING_COUNTRY_CODE:
-        case PHONE_BILLING_CITY_AND_NUMBER:
-        case PHONE_BILLING_WHOLE_NUMBER:
-        case NAME_BILLING_FIRST:
-        case NAME_BILLING_MIDDLE:
-        case NAME_BILLING_LAST:
-        case NAME_BILLING_MIDDLE_INITIAL:
-        case NAME_BILLING_FULL:
-        case NAME_BILLING_SUFFIX:
         case MERCHANT_EMAIL_SIGNUP:
         case MERCHANT_PROMO_CODE:
         case PASSWORD:
         case ACCOUNT_CREATION_PASSWORD:
-        case ADDRESS_BILLING_STREET_ADDRESS:
-        case ADDRESS_BILLING_SORTING_CODE:
-        case ADDRESS_BILLING_DEPENDENT_LOCALITY:
-        case ADDRESS_BILLING_LINE3:
         case NOT_ACCOUNT_CREATION_PASSWORD:
         case USERNAME:
         case USERNAME_AND_EMAIL_ADDRESS:
diff --git a/components/autofill/core/browser/rationalization_util.cc b/components/autofill/core/browser/rationalization_util.cc
index 1d606e4..243e3f0 100644
--- a/components/autofill/core/browser/rationalization_util.cc
+++ b/components/autofill/core/browser/rationalization_util.cc
@@ -30,7 +30,6 @@
     ServerFieldType current_field_type = field->Type().GetStorableType();
     switch (current_field_type) {
       case PHONE_HOME_NUMBER:
-      case PHONE_BILLING_NUMBER:
         if (!found_number_field) {
           found_number_field = field;
           if (field->max_length < 5) {
@@ -51,24 +50,20 @@
         break;
       case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
       case PHONE_HOME_CITY_CODE:
-      case PHONE_BILLING_CITY_CODE:
         if (!found_city_code_field)
           found_city_code_field = field;
         break;
       case PHONE_HOME_COUNTRY_CODE:
-      case PHONE_BILLING_COUNTRY_CODE:
         if (!found_country_code_field)
           found_country_code_field = field;
         break;
       case PHONE_HOME_CITY_AND_NUMBER:
       case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
-      case PHONE_BILLING_CITY_AND_NUMBER:
         DCHECK(!phone_number_found && !found_city_and_number_field);
         found_city_and_number_field = field;
         phone_number_found = true;
         break;
       case PHONE_HOME_WHOLE_NUMBER:
-      case PHONE_BILLING_WHOLE_NUMBER:
         DCHECK(!phone_number_found && !found_whole_number_field);
         found_whole_number_field = field;
         phone_number_found = true;
@@ -125,29 +120,24 @@
     ServerFieldType current_field_type = field->Type().GetStorableType();
     switch (current_field_type) {
       case PHONE_HOME_NUMBER:
-      case PHONE_BILLING_NUMBER:
         if (field != found_number_field && field != found_number_field_second)
           field->set_only_fill_when_focused(true);
         break;
       case PHONE_HOME_CITY_CODE:
       case PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
-      case PHONE_BILLING_CITY_CODE:
         if (field != found_city_code_field)
           field->set_only_fill_when_focused(true);
         break;
       case PHONE_HOME_COUNTRY_CODE:
-      case PHONE_BILLING_COUNTRY_CODE:
         if (field != found_country_code_field)
           field->set_only_fill_when_focused(true);
         break;
       case PHONE_HOME_CITY_AND_NUMBER:
       case PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
-      case PHONE_BILLING_CITY_AND_NUMBER:
         if (field != found_city_and_number_field)
           field->set_only_fill_when_focused(true);
         break;
       case PHONE_HOME_WHOLE_NUMBER:
-      case PHONE_BILLING_WHOLE_NUMBER:
         if (field != found_whole_number_field)
           field->set_only_fill_when_focused(true);
         break;
diff --git a/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc
index cb85212..40e81a1 100644
--- a/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/ui/address_contact_form_label_formatter_unittest.cc
@@ -23,23 +23,18 @@
 namespace {
 
 std::vector<ServerFieldType> GetFieldTypes() {
-  return {NO_SERVER_DATA,
-          NAME_BILLING_FULL,
-          EMAIL_ADDRESS,
-          ADDRESS_BILLING_LINE1,
-          ADDRESS_BILLING_LINE2,
-          ADDRESS_BILLING_DEPENDENT_LOCALITY,
-          ADDRESS_BILLING_CITY,
-          ADDRESS_BILLING_STATE,
-          ADDRESS_BILLING_ZIP,
-          ADDRESS_BILLING_COUNTRY,
-          PHONE_BILLING_WHOLE_NUMBER};
+  return {NO_SERVER_DATA,         NAME_FULL,
+          EMAIL_ADDRESS,          ADDRESS_HOME_LINE1,
+          ADDRESS_HOME_LINE2,     ADDRESS_HOME_DEPENDENT_LOCALITY,
+          ADDRESS_HOME_CITY,      ADDRESS_HOME_STATE,
+          ADDRESS_HOME_ZIP,       ADDRESS_HOME_COUNTRY,
+          PHONE_HOME_WHOLE_NUMBER};
 }
 
 TEST(AddressContactFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
   const std::vector<AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
   EXPECT_TRUE(formatter->GetLabels().empty());
 }
 
@@ -80,8 +75,8 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4, &profile5, &profile6};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -131,7 +126,7 @@
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4, &profile5, &profile6};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_LINE1, GetFieldTypes());
+      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -182,7 +177,7 @@
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4, &profile5, &profile6};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_CITY, GetFieldTypes());
+      profiles, "en-US", ADDRESS_HOME_CITY, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -283,7 +278,7 @@
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4, &profile5, &profile6};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", PHONE_BILLING_WHOLE_NUMBER, GetFieldTypes());
+      profiles, "en-US", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -313,8 +308,8 @@
                        "21987650000");
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -343,7 +338,7 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_BILLING_LINE1, GetFieldTypes());
+      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -371,7 +366,7 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_BILLING_ZIP, GetFieldTypes());
+      profiles, "pt-BR", ADDRESS_HOME_ZIP, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -429,7 +424,7 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", PHONE_BILLING_WHOLE_NUMBER, GetFieldTypes());
+      profiles, "pt-BR", PHONE_HOME_WHOLE_NUMBER, GetFieldTypes());
 
   EXPECT_THAT(formatter->GetLabels(),
               ElementsAre(ConstructLabelLine({u"Tarsila do Amaral",
@@ -449,10 +444,9 @@
                        "16175232338");
 
   const std::vector<AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS,
-                             {NAME_BILLING_FULL, EMAIL_ADDRESS,
-                              ADDRESS_BILLING_ZIP, PHONE_BILLING_WHOLE_NUMBER});
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      profiles, "en-US", EMAIL_ADDRESS,
+      {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER});
 
   // Checks that only address fields in the form are shown in the label.
   EXPECT_THAT(formatter->GetLabels(),
@@ -469,18 +463,17 @@
 
   std::vector<AutofillProfile*> profiles{&profile1};
   std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_LINE1,
-      {ADDRESS_BILLING_ZIP, EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER});
+      profiles, "en-US", ADDRESS_HOME_LINE1,
+      {ADDRESS_HOME_ZIP, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
 
   // Checks that the name is not in the label and that the phone number is for
   // a unique profile.
   EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338"));
 
   profiles = {&profile1, &profile1};
-  formatter =
-      LabelFormatter::Create(profiles, "en-US", ADDRESS_BILLING_LINE1,
-                             {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP,
-                              EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER});
+  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
+                                     {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP,
+                                      EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
 
   // Checks that the name is not in the label and that the phone number is for
   // multiple profiles with the same phone number and email address.
@@ -494,10 +487,9 @@
                        "16175232338");
 
   profiles = {&profile1, &profile2};
-  formatter =
-      LabelFormatter::Create(profiles, "en-US", ADDRESS_BILLING_LINE1,
-                             {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP,
-                              EMAIL_ADDRESS, PHONE_BILLING_WHOLE_NUMBER});
+  formatter = LabelFormatter::Create(profiles, "en-US", ADDRESS_HOME_LINE1,
+                                     {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP,
+                                      EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER});
   // Checks that the name is not in the label and that the email address is
   // shown because the profiles' email addresses are different.
   EXPECT_THAT(formatter->GetLabels(),
diff --git a/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc
index a4d3b823..6474e33 100644
--- a/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/ui/address_email_form_label_formatter_unittest.cc
@@ -25,19 +25,19 @@
 std::vector<ServerFieldType> GetFieldTypes() {
   return {NAME_FULL,
           EMAIL_ADDRESS,
-          ADDRESS_BILLING_LINE1,
-          ADDRESS_BILLING_LINE2,
-          ADDRESS_BILLING_CITY,
-          ADDRESS_BILLING_STATE,
-          ADDRESS_BILLING_DEPENDENT_LOCALITY,
-          ADDRESS_BILLING_ZIP,
-          ADDRESS_BILLING_COUNTRY};
+          ADDRESS_HOME_LINE1,
+          ADDRESS_HOME_LINE2,
+          ADDRESS_HOME_CITY,
+          ADDRESS_HOME_STATE,
+          ADDRESS_HOME_DEPENDENT_LOCALITY,
+          ADDRESS_HOME_ZIP,
+          ADDRESS_HOME_COUNTRY};
 }
 
 TEST(AddressEmailFormLabelFormatterTest, GetLabelsWithMissingProfiles) {
   const std::vector<AutofillProfile*> profiles{};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
   EXPECT_TRUE(formatter->GetLabels().empty());
 }
 
@@ -66,8 +66,8 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "en-US", NAME_FULL, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -102,7 +102,7 @@
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_LINE1, GetFieldTypes());
+      profiles, "en-US", ADDRESS_HOME_LINE1, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -137,7 +137,7 @@
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2, &profile3,
                                                &profile4};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_ZIP, GetFieldTypes());
+      profiles, "en-US", ADDRESS_HOME_ZIP, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -195,8 +195,8 @@
                        "21987650000");
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
-  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", NAME_BILLING_FULL, GetFieldTypes());
+  const std::unique_ptr<LabelFormatter> formatter =
+      LabelFormatter::Create(profiles, "pt-BR", NAME_FULL, GetFieldTypes());
 
   EXPECT_THAT(formatter->GetLabels(),
               ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
@@ -223,7 +223,7 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_BILLING_LINE1, GetFieldTypes());
+      profiles, "pt-BR", ADDRESS_HOME_LINE1, GetFieldTypes());
 
   EXPECT_THAT(
       formatter->GetLabels(),
@@ -250,7 +250,7 @@
 
   const std::vector<AutofillProfile*> profiles{&profile1, &profile2};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "pt-BR", ADDRESS_BILLING_DEPENDENT_LOCALITY, GetFieldTypes());
+      profiles, "pt-BR", ADDRESS_HOME_DEPENDENT_LOCALITY, GetFieldTypes());
 
   EXPECT_THAT(formatter->GetLabels(),
               ElementsAre(ConstructLabelLine({u"Av. Pedro Álvares Cabral, 1301",
@@ -296,10 +296,9 @@
                        "US", "16177302000");
 
   const std::vector<AutofillProfile*> profiles{&profile};
-  const std::unique_ptr<LabelFormatter> formatter =
-      LabelFormatter::Create(profiles, "en-US", EMAIL_ADDRESS,
-                             {NAME_BILLING_FULL, EMAIL_ADDRESS,
-                              ADDRESS_BILLING_CITY, ADDRESS_BILLING_STATE});
+  const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
+      profiles, "en-US", EMAIL_ADDRESS,
+      {NAME_FULL, EMAIL_ADDRESS, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE});
 
   // Checks that only address fields in the form are shown in the label.
   EXPECT_THAT(
@@ -316,8 +315,8 @@
 
   const std::vector<AutofillProfile*> profiles{&profile};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_LINE1,
-      {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, EMAIL_ADDRESS});
+      profiles, "en-US", ADDRESS_HOME_LINE1,
+      {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, EMAIL_ADDRESS});
 
   // Checks that the name does not appear in the labels.
   EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"sarah.revere@aol.com"));
diff --git a/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc
index fd0569e..9936f8e 100644
--- a/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/ui/address_phone_form_label_formatter_unittest.cc
@@ -310,12 +310,12 @@
 
   const std::vector<AutofillProfile*> profiles{&profile};
   const std::unique_ptr<LabelFormatter> formatter = LabelFormatter::Create(
-      profiles, "en-US", ADDRESS_BILLING_LINE1,
-      {ADDRESS_BILLING_LINE1, ADDRESS_BILLING_ZIP, PHONE_HOME_WHOLE_NUMBER});
+      profiles, "en-US", ADDRESS_HOME_LINE1,
+      {ADDRESS_HOME_LINE1, ADDRESS_HOME_ZIP, PHONE_HOME_WHOLE_NUMBER});
 
   // Checks that the name does not appear in the labels.
   EXPECT_THAT(formatter->GetLabels(), ElementsAre(u"(617) 523-2338"));
 }
 
 }  // namespace
-}  // namespace autofill
\ No newline at end of file
+}  // namespace autofill
diff --git a/components/autofill/core/browser/ui/label_formatter.cc b/components/autofill/core/browser/ui/label_formatter.cc
index 9aa9931..0e0eae7 100644
--- a/components/autofill/core/browser/ui/label_formatter.cc
+++ b/components/autofill/core/browser/ui/label_formatter.cc
@@ -40,7 +40,7 @@
       app_locale_(app_locale),
       focused_field_type_(focused_field_type),
       groups_(groups) {
-  const FieldTypeGroup focused_group = GetFocusedNonBillingGroup();
+  const FieldTypeGroup focused_group = AutofillType(focused_field_type).group();
   DenseSet<FieldTypeGroup> groups_for_labels{
       FieldTypeGroup::kName, FieldTypeGroup::kAddressHome,
       FieldTypeGroup::kEmail, FieldTypeGroup::kPhoneHome};
@@ -60,7 +60,7 @@
     return groups_for_labels.find(
                AutofillType(AutofillType(type).GetStorableType()).group()) !=
                groups_for_labels.end() &&
-           type != ADDRESS_HOME_COUNTRY && type != ADDRESS_BILLING_COUNTRY;
+           type != ADDRESS_HOME_COUNTRY;
   };
 
   std::copy_if(field_types.begin(), field_types.end(),
@@ -73,16 +73,12 @@
 std::vector<std::u16string> LabelFormatter::GetLabels() const {
   std::vector<std::u16string> labels;
   for (const AutofillProfile* profile : profiles_) {
-    labels.push_back(GetLabelForProfile(*profile, GetFocusedNonBillingGroup()));
+    labels.push_back(GetLabelForProfile(
+        *profile, AutofillType(focused_field_type_).group()));
   }
   return labels;
 }
 
-FieldTypeGroup LabelFormatter::GetFocusedNonBillingGroup() const {
-  return AutofillType(AutofillType(focused_field_type_).GetStorableType())
-      .group();
-}
-
 // static
 std::unique_ptr<LabelFormatter> LabelFormatter::Create(
     const std::vector<AutofillProfile*>& profiles,
diff --git a/components/autofill/core/browser/ui/label_formatter.h b/components/autofill/core/browser/ui/label_formatter.h
index cf66bfb..8bb5a3b 100644
--- a/components/autofill/core/browser/ui/label_formatter.h
+++ b/components/autofill/core/browser/ui/label_formatter.h
@@ -49,12 +49,6 @@
       const AutofillProfile& profile,
       FieldTypeGroup focused_group) const = 0;
 
-  // Returns the FieldTypeGroup with which |focused_field_type_| is associated.
-  // Billing field types are mapped to their corresponding home address field
-  // types. For example, if focused_field_type_ is ADDRESS_BILLING_ZIP, then
-  // the resulting FieldTypeGroup is kAddressHome instead of kAddressBilling.
-  FieldTypeGroup GetFocusedNonBillingGroup() const;
-
   const std::string& app_locale() const { return app_locale_; }
 
   ServerFieldType focused_field_type() const { return focused_field_type_; }
diff --git a/components/autofill/core/browser/ui/label_formatter_utils.cc b/components/autofill/core/browser/ui/label_formatter_utils.cc
index f7d0ced..506df6e 100644
--- a/components/autofill/core/browser/ui/label_formatter_utils.cc
+++ b/components/autofill/core/browser/ui/label_formatter_utils.cc
@@ -86,17 +86,11 @@
 bool IsNonStreetAddressPart(ServerFieldType type) {
   switch (type) {
     case ADDRESS_HOME_CITY:
-    case ADDRESS_BILLING_CITY:
     case ADDRESS_HOME_ZIP:
-    case ADDRESS_BILLING_ZIP:
     case ADDRESS_HOME_STATE:
-    case ADDRESS_BILLING_STATE:
     case ADDRESS_HOME_COUNTRY:
-    case ADDRESS_BILLING_COUNTRY:
     case ADDRESS_HOME_SORTING_CODE:
-    case ADDRESS_BILLING_SORTING_CODE:
     case ADDRESS_HOME_DEPENDENT_LOCALITY:
-    case ADDRESS_BILLING_DEPENDENT_LOCALITY:
       return true;
     default:
       return false;
@@ -108,13 +102,8 @@
     case ADDRESS_HOME_LINE1:
     case ADDRESS_HOME_LINE2:
     case ADDRESS_HOME_APT_NUM:
-    case ADDRESS_BILLING_LINE1:
-    case ADDRESS_BILLING_LINE2:
-    case ADDRESS_BILLING_APT_NUM:
     case ADDRESS_HOME_STREET_ADDRESS:
-    case ADDRESS_BILLING_STREET_ADDRESS:
     case ADDRESS_HOME_LINE3:
-    case ADDRESS_BILLING_LINE3:
       return true;
     default:
       return false;
diff --git a/components/autofill/core/browser/ui/mobile_label_formatter.cc b/components/autofill/core/browser/ui/mobile_label_formatter.cc
index 09f00ce4..a37b023 100644
--- a/components/autofill/core/browser/ui/mobile_label_formatter.cc
+++ b/components/autofill/core/browser/ui/mobile_label_formatter.cc
@@ -35,7 +35,7 @@
                      focused_field_type,
                      groups,
                      field_types) {
-  const FieldTypeGroup focused_group = GetFocusedNonBillingGroup();
+  const FieldTypeGroup focused_group = AutofillType(focused_field_type).group();
 
   could_show_email_ = HasUnfocusedEmailField(focused_group, groups) &&
                       !HaveSameEmailAddresses(profiles, app_locale);
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
index ec4b4b7..3a52741b 100644
--- a/components/exo/buffer.cc
+++ b/components/exo/buffer.cc
@@ -508,8 +508,6 @@
                                                   sync_token, texture_target_);
     resource->is_overlay_candidate = is_overlay_candidate_;
     resource->format = viz::GetResourceFormat(gpu_memory_buffer_->GetFormat());
-    resource->synchronization_type =
-        viz::TransferableResource::SynchronizationType::kReleaseFence;
 
     // The contents texture will be released when no longer used by the
     // compositor.
diff --git a/components/exo/test/shell_surface_builder.cc b/components/exo/test/shell_surface_builder.cc
index 12f37e3..cd03f31 100644
--- a/components/exo/test/shell_surface_builder.cc
+++ b/components/exo/test/shell_surface_builder.cc
@@ -9,9 +9,10 @@
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "components/exo/buffer.h"
+#include "components/exo/display.h"
 #include "components/exo/sub_surface.h"
 #include "components/exo/surface.h"
-#include "components/exo/test/exo_test_helper.h"
+#include "components/exo/test/exo_test_base.h"
 #include "components/exo/xdg_shell_surface.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "ui/aura/env.h"
@@ -128,12 +129,6 @@
   return *this;
 }
 
-ShellSurfaceBuilder& ShellSurfaceBuilder::SetParent(ShellSurface* parent) {
-  DCHECK(!built_);
-  parent_shell_surface_ = parent;
-  return *this;
-}
-
 ShellSurfaceBuilder& ShellSurfaceBuilder::SetUseSystemModalContainer() {
   DCHECK(!built_);
   use_system_modal_container_ = true;
@@ -178,12 +173,24 @@
   return *this;
 }
 
+ShellSurfaceBuilder& ShellSurfaceBuilder::SetParent(ShellSurface* parent) {
+  DCHECK(!built_);
+  parent_shell_surface_ = parent;
+  return *this;
+}
+
 ShellSurfaceBuilder& ShellSurfaceBuilder::SetAsPopup() {
   DCHECK(!built_);
   popup_ = true;
   return *this;
 }
 
+ShellSurfaceBuilder& ShellSurfaceBuilder::EnableDefaultScaleCancellation() {
+  DCHECK(!built_);
+  default_scale_cancellation_ = true;
+  return *this;
+}
+
 // static
 void ShellSurfaceBuilder::DestroyRootSurface(ShellSurfaceBase* shell_surface) {
   Holder* holder =
@@ -201,44 +208,89 @@
 }
 
 std::unique_ptr<ShellSurface> ShellSurfaceBuilder::BuildShellSurface() {
+  // Create a ShellSurface instance.
   DCHECK(!built_);
+  DCHECK(isConfigurationValidForShellSurface());
   built_ = true;
   Holder* holder = new Holder();
   holder->AddRootSurface(root_buffer_size_, root_buffer_format_);
-
-  int container = use_system_modal_container_
-                      ? ash::kShellWindowId_SystemModalContainer
-                      : ash::desks_util::GetActiveDeskContainerId();
-
   auto shell_surface = std::make_unique<ShellSurface>(
-      holder->root_surface, origin_, can_minimize_, container);
+      holder->root_surface, origin_, can_minimize_, GetContainer());
   shell_surface->host_window()->SetProperty(kBuilderResourceHolderKey, holder);
 
+  // Set the properties specific to ShellSurface.
   if (parent_shell_surface_)
     shell_surface->SetParent(parent_shell_surface_);
-
-  if (disable_movement_)
-    shell_surface->DisableMovement();
-
-  if (!max_size_.IsEmpty())
-    shell_surface->SetMaximumSize(max_size_);
-
-  if (!min_size_.IsEmpty())
-    shell_surface->SetMaximumSize(min_size_);
-
   if (popup_)
     shell_surface->SetPopup();
 
+  SetCommonPropertiesAndCommitIfNecessary(shell_surface.get());
+
+  return shell_surface;
+}
+
+std::unique_ptr<ClientControlledShellSurface>
+ShellSurfaceBuilder::BuildClientControlledShellSurface() {
+  // Create a ClientControlledShellSurface instance.
+  DCHECK(!built_);
+  DCHECK(isConfigurationValidForClientControlledShellSurface());
+  built_ = true;
+  Holder* holder = new Holder();
+  holder->AddRootSurface(root_buffer_size_, root_buffer_format_);
+  auto shell_surface = Display().CreateOrGetClientControlledShellSurface(
+      holder->root_surface, GetContainer(),
+      WMHelper::GetInstance()->GetDefaultDeviceScaleFactor(),
+      default_scale_cancellation_);
+  shell_surface->host_window()->SetProperty(kBuilderResourceHolderKey, holder);
+
+  // Set the properties specific to ClientControlledShellSurface.
+  shell_surface->SetApplicationId("arc");
+  // ARC's default min size is non-empty.
+  if (!min_size_.has_value())
+    shell_surface->SetMinimumSize(gfx::Size(1, 1));
+  shell_surface->set_delegate(
+      std::make_unique<ClientControlledShellSurfaceDelegate>(
+          shell_surface.get()));
+
+  SetCommonPropertiesAndCommitIfNecessary(shell_surface.get());
+
+  return shell_surface;
+}
+
+bool ShellSurfaceBuilder::isConfigurationValidForShellSurface() {
+  return !default_scale_cancellation_;
+}
+
+bool ShellSurfaceBuilder::
+    isConfigurationValidForClientControlledShellSurface() {
+  return !parent_shell_surface_ && !popup_;
+}
+
+void ShellSurfaceBuilder::SetCommonPropertiesAndCommitIfNecessary(
+    ShellSurfaceBase* shell_surface) {
+  if (disable_movement_)
+    shell_surface->DisableMovement();
+
+  if (max_size_.has_value())
+    shell_surface->SetMaximumSize(max_size_.value());
+
+  if (min_size_.has_value())
+    shell_surface->SetMinimumSize(min_size_.value());
+
   if (commit_on_build_) {
-    holder->root_surface->Commit();
+    shell_surface->root_surface()->Commit();
     if (centered_)
       ash::CenterWindow(shell_surface->GetWidget()->GetNativeWindow());
   } else {
     // 'SetCentered' requires its shell surface to be committed when creatted.
     DCHECK(!centered_);
   }
+}
 
-  return shell_surface;
+int ShellSurfaceBuilder::GetContainer() {
+  return use_system_modal_container_
+             ? ash::kShellWindowId_SystemModalContainer
+             : ash::desks_util::GetActiveDeskContainerId();
 }
 
 }  // namespace test
diff --git a/components/exo/test/shell_surface_builder.h b/components/exo/test/shell_surface_builder.h
index 2eab3e5b..c4e8580 100644
--- a/components/exo/test/shell_surface_builder.h
+++ b/components/exo/test/shell_surface_builder.h
@@ -14,16 +14,17 @@
 #include "ui/gfx/geometry/size.h"
 
 namespace exo {
+class ClientControlledShellSurface;
 class ShellSurface;
 class ShellSurfaceBase;
 class Surface;
 
 namespace test {
 
-// A builder to create a ShellSurface for testing purpose. Its surface and
-// buffer, which are typically owned by a client, are owned by the host window
-// as an owned property, therefore are destroyed when the shell surface is
-// destroyed.
+// A builder to create a ShellSurface or ClientControlledShellSurface for
+// testing purpose. Its surface and buffer, which are typically owned by a
+// client, are owned by the host window as an owned property, therefore are
+// destroyed when the shell surface is destroyed.
 class ShellSurfaceBuilder {
  public:
   ShellSurfaceBuilder(const gfx::Size& buffer_size);
@@ -31,22 +32,30 @@
   ShellSurfaceBuilder& operator=(ShellSurfaceBuilder& other) = delete;
   ~ShellSurfaceBuilder();
 
-  // Sets parameters that are used when creating a test window.
+  // Sets parameters common for all ShellSurfaceType.
   ShellSurfaceBuilder& SetNoRootBuffer();
   ShellSurfaceBuilder& SetRootBufferFormat(gfx::BufferFormat buffer_format);
   ShellSurfaceBuilder& SetOrigin(const gfx::Point& origin);
-  ShellSurfaceBuilder& SetParent(ShellSurface* shell_surface);
   ShellSurfaceBuilder& SetUseSystemModalContainer();
   ShellSurfaceBuilder& SetNoCommit();
   ShellSurfaceBuilder& SetCanMinimize(bool can_minimize);
   ShellSurfaceBuilder& SetMaximumSize(const gfx::Size& size);
   ShellSurfaceBuilder& SetMinimumSize(const gfx::Size& size);
   ShellSurfaceBuilder& SetDisableMovement();
-  ShellSurfaceBuilder& SetAsPopup();
   ShellSurfaceBuilder& SetCentered();
 
-  // once and the object cannot be used to create multiple windows.
+  // Sets parameters defined in ShellSurface.
+  ShellSurfaceBuilder& SetParent(ShellSurface* shell_surface);
+  ShellSurfaceBuilder& SetAsPopup();
+
+  // Sets parameters defined in ClientControlledShellSurface.
+  ShellSurfaceBuilder& EnableDefaultScaleCancellation();
+
+  // Must be called only once for either of the below and the object cannot
+  // be used to create multiple windows.
   [[nodiscard]] std::unique_ptr<ShellSurface> BuildShellSurface();
+  [[nodiscard]] std::unique_ptr<ClientControlledShellSurface>
+  BuildClientControlledShellSurface();
 
   // Destroy's the root surface of the given 'shell_surface'.
   static void DestroyRootSurface(ShellSurfaceBase* shell_surface);
@@ -54,21 +63,30 @@
                                   const gfx::Rect& bounds);
 
  private:
+  bool isConfigurationValidForShellSurface();
+  bool isConfigurationValidForClientControlledShellSurface();
+  void SetCommonPropertiesAndCommitIfNecessary(ShellSurfaceBase* shell_surface);
+  int GetContainer();
+
   gfx::Size root_buffer_size_;
   absl::optional<gfx::BufferFormat> root_buffer_format_ =
       gfx::BufferFormat::RGBA_8888;
   gfx::Point origin_;
-  gfx::Size max_size_;
-  gfx::Size min_size_;
-  ShellSurface* parent_shell_surface_ = nullptr;
+  absl::optional<gfx::Size> max_size_;
+  absl::optional<gfx::Size> min_size_;
   bool use_system_modal_container_ = false;
   bool commit_on_build_ = true;
   bool can_minimize_ = true;
   bool disable_movement_ = false;
   bool centered_ = false;
+  bool built_ = false;
+
+  // ShellSurface-specific parameters.
+  ShellSurface* parent_shell_surface_ = nullptr;
   bool popup_ = false;
 
-  bool built_ = false;
+  // ClientControlledShellSurface-specific parameters.
+  bool default_scale_cancellation_ = false;
 };
 
 }  // namespace test
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index b294e07..5963fd95 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -427,14 +427,6 @@
     HandleIdleSinceLastTransmission(false);
 }
 
-void MetricsService::RecordStartOfSessionEnd() {
-  LogCleanShutdown(false);
-}
-
-void MetricsService::RecordCompletedSessionEnd() {
-  LogCleanShutdown(true);
-}
-
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 void MetricsService::OnAppEnterBackground(bool keep_recording_in_background) {
   is_in_foreground_ = false;
@@ -478,6 +470,11 @@
 }
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 
+void MetricsService::LogCleanShutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  state_manager_->LogHasSessionShutdownCleanly(true);
+}
+
 void MetricsService::ClearSavedStabilityMetrics() {
   delegating_provider_.ClearSavedStabilityMetrics();
   // Stability metrics are stored in Local State prefs, so schedule a Local
@@ -1095,11 +1092,6 @@
       next_check);
 }
 
-void MetricsService::LogCleanShutdown(bool end_completed) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  state_manager_->LogHasSessionShutdownCleanly(true);
-}
-
 void MetricsService::UpdateLastLiveTimestampTask() {
   state_manager_->clean_exit_beacon()->UpdateLastLiveTimestamp();
 
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index 6a264da..0963e20 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -135,14 +135,6 @@
   // to be interacting with the application.
   void OnApplicationNotIdle();
 
-  // Invoked when we get a WM_SESSIONEND. This places a value in prefs that is
-  // reset when RecordCompletedSessionEnd is invoked.
-  void RecordStartOfSessionEnd();
-
-  // This should be called when the application is shutting down. It records
-  // that session end was successful.
-  void RecordCompletedSessionEnd();
-
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
   // Called when the application is going into background mode.
   // If |keep_recording_in_background| is true, UMA is still recorded and
@@ -153,6 +145,10 @@
   void OnAppEnterForeground(bool force_open_new_log = false);
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
 
+  // Signals that the browser is shutting down cleanly. Intended to be called
+  // during shutdown after critical shutdown tasks have completed.
+  void LogCleanShutdown();
+
   bool recording_active() const;
   bool reporting_active() const;
   bool has_unsent_logs() const;
@@ -375,9 +371,6 @@
   // stored as a string.
   void IncrementLongPrefsValue(const char* path);
 
-  // Records that the browser was shut down cleanly.
-  void LogCleanShutdown(bool end_completed);
-
   // Creates a new MetricsLog instance with the given |log_type|.
   std::unique_ptr<MetricsLog> CreateLog(MetricsLog::LogType log_type);
 
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc
index 0a3651c..bf2f762 100644
--- a/components/network_time/network_time_tracker.cc
+++ b/components/network_time/network_time_tracker.cc
@@ -488,6 +488,7 @@
   resource_request->load_flags =
       net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->enable_load_timing = true;
   // This cancels any outstanding fetch.
   time_fetcher_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                    traffic_annotation);
@@ -498,8 +499,6 @@
                      base::Unretained(this), check_type),
       max_response_size_);
 
-  fetch_started_ = tick_clock_->NowTicks();
-
   timer_.Stop();  // Restarted in OnURLLoaderComplete().
 }
 
@@ -561,7 +560,9 @@
 
   // Record histograms for the latency of the time query and the time delta
   // between time fetches.
-  base::TimeDelta latency = tick_clock_->NowTicks() - fetch_started_;
+  base::TimeDelta latency =
+      time_fetcher_->ResponseInfo()->load_timing.receive_headers_start -
+      time_fetcher_->ResponseInfo()->load_timing.send_end;
   LOCAL_HISTOGRAM_TIMES("NetworkTimeTracker.TimeQueryLatency", latency);
 
   if (!last_fetched_time_.is_null()) {
diff --git a/components/network_time/network_time_tracker.h b/components/network_time/network_time_tracker.h
index 01c4a04..7671af9 100644
--- a/components/network_time/network_time_tracker.h
+++ b/components/network_time/network_time_tracker.h
@@ -234,7 +234,6 @@
   base::RepeatingTimer timer_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::unique_ptr<network::SimpleURLLoader> time_fetcher_;
-  base::TimeTicks fetch_started_;
   std::unique_ptr<client_update_protocol::Ecdsa> query_signer_;
 
   // The |Clock| and |TickClock| are used to sanity-check one another, allowing
diff --git a/components/network_time/network_time_tracker_unittest.cc b/components/network_time/network_time_tracker_unittest.cc
index 5435c88..636e45d 100644
--- a/components/network_time/network_time_tracker_unittest.cc
+++ b/components/network_time/network_time_tracker_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback_forward.h"
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -25,7 +26,13 @@
 #include "net/http/http_response_headers.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/url_loader_completion_status.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/test/test_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace network_time {
@@ -42,6 +49,20 @@
     "NetworkTimeTracker.WallClockRanBackwards";
 const char kTimeBetweenFetchesHistogram[] =
     "NetworkTimeTracker.TimeBetweenFetches";
+
+// Latencies simulated by the fake network responses for the network times. This
+// array should have the same length as `kGoodTimeResponseBody`.
+const base::TimeDelta kGoodTimeResponseLatency[] = {
+    base::Milliseconds(500), base::Milliseconds(520), base::Milliseconds(450),
+    base::Milliseconds(550), base::Milliseconds(480),
+};
+
+struct MockedResponse {
+  network::mojom::URLResponseHeadPtr head;
+  std::string body;
+  network::URLLoaderCompletionStatus status;
+};
+
 }  // namespace
 
 class NetworkTimeTrackerTest : public ::testing::Test {
@@ -53,8 +74,7 @@
             base::test::SingleThreadTaskEnvironment::MainThreadType::IO),
         field_trial_test_(new FieldTrialTest()),
         clock_(new base::SimpleTestClock),
-        tick_clock_(new base::SimpleTestTickClock),
-        test_server_(new net::EmbeddedTestServer) {
+        tick_clock_(new base::SimpleTestTickClock) {
     NetworkTimeTracker::RegisterPrefs(pref_service_.registry());
 
     field_trial_test_->SetFeatureParams(
@@ -62,13 +82,14 @@
         NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
         NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
 
-    shared_url_loader_factory_ =
-        base::MakeRefCounted<network::TestSharedURLLoaderFactory>();
+    url_loader_factory_.SetInterceptor(base::BindRepeating(
+        &NetworkTimeTrackerTest::Intercept, weak_ptr_factory_.GetWeakPtr()));
 
     tracker_ = std::make_unique<NetworkTimeTracker>(
         std::unique_ptr<base::Clock>(clock_),
         std::unique_ptr<const base::TickClock>(tick_clock_), &pref_service_,
-        shared_url_loader_factory_);
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &url_loader_factory_));
 
     // Do this to be sure that |is_null| returns false.
     clock_->Advance(base::Days(111));
@@ -80,6 +101,13 @@
     adjustment_ = 7 * base::Milliseconds(kTicksResolutionMs);
   }
 
+  // Sets `response_handler` as handler for all requests made through
+  // `url_loader_factory_`.
+  void SetResponseHandler(
+      base::RepeatingCallback<MockedResponse()> response_handler) {
+    response_handler_ = std::move(response_handler);
+  }
+
   // Replaces |tracker_| with a new object, while preserving the
   // testing clocks.
   void Reset() {
@@ -92,53 +120,58 @@
     tracker_ = std::make_unique<NetworkTimeTracker>(
         std::unique_ptr<base::Clock>(clock_),
         std::unique_ptr<const base::TickClock>(tick_clock_), &pref_service_,
-        shared_url_loader_factory_);
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &url_loader_factory_));
   }
 
   // Good signature over invalid data, though made with a non-production key.
-  static std::unique_ptr<net::test_server::HttpResponse> BadDataResponseHandler(
-      const net::test_server::HttpRequest& request) {
-    net::test_server::BasicHttpResponse* response =
-        new net::test_server::BasicHttpResponse();
-    response->set_code(net::HTTP_OK);
-    response->set_content(
+  static MockedResponse BadDataResponseHandler() {
+    network::mojom::URLResponseHeadPtr head =
+        network::CreateURLResponseHead(net::HTTP_OK);
+    std::string body =
         ")]}'\n"
-        "{\"current_time_millis\":NaN,\"server_nonce\":9.420921002039447E182}");
-    response->AddCustomHeader(
+        "{\"current_time_millis\":NaN,\"server_nonce\":9.420921002039447E182}";
+    head->headers->AddHeader(
         "x-cup-server-proof",
         "3046022100a07aa437b24f1f6bb7ff6f6d1e004dd4bcb717c93e21d6bae5ef8d6d984c"
         "86a7022100e423419ff49fae37b421ef6cdeab348b45c63b236ab365f36f4cd3b4d4d6"
         "d852:"
         "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85"
         "5");
-    return std::unique_ptr<net::test_server::HttpResponse>(response);
+    return MockedResponse{std::move(head), std::move(body)};
   }
 
-  static std::unique_ptr<net::test_server::HttpResponse>
-  BadSignatureResponseHandler(const net::test_server::HttpRequest& request) {
-    net::test_server::BasicHttpResponse* response =
-        new net::test_server::BasicHttpResponse();
-    response->set_code(net::HTTP_OK);
-    response->set_content(
+  static MockedResponse GoodTimeResponseHandler() {
+    network::mojom::URLResponseHeadPtr head =
+        network::CreateURLResponseHead(net::HTTP_OK);
+    head->headers->AddHeader("x-cup-server-proof",
+                             kGoodTimeResponseServerProofHeader[0]);
+    return MockedResponse{std::move(head), kGoodTimeResponseBody[0]};
+  }
+
+  static MockedResponse BadSignatureResponseHandler() {
+    network::mojom::URLResponseHeadPtr head =
+        network::CreateURLResponseHead(net::HTTP_OK);
+    std::string body =
         ")]}'\n"
         "{\"current_time_millis\":1461621971825,\"server_nonce\":-6."
-        "006853099049523E85}");
-    response->AddCustomHeader("x-cup-server-proof", "dead:beef");
-    return std::unique_ptr<net::test_server::HttpResponse>(response);
+        "006853099049523E85}";
+    head->headers->AddHeader("x-cup-server-proof", "dead:beef");
+    return MockedResponse{std::move(head), std::move(body)};
   }
 
-  static std::unique_ptr<net::test_server::HttpResponse>
-  ServerErrorResponseHandler(const net::test_server::HttpRequest& request) {
-    net::test_server::BasicHttpResponse* response =
-        new net::test_server::BasicHttpResponse();
-    response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
-    return std::unique_ptr<net::test_server::HttpResponse>(response);
+  static MockedResponse ServerErrorResponseHandler() {
+    network::mojom::URLResponseHeadPtr head =
+        network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR);
+    return MockedResponse{std::move(head), ""};
   }
 
-  static std::unique_ptr<net::test_server::HttpResponse>
-  NetworkErrorResponseHandler(const net::test_server::HttpRequest& request) {
-    return std::unique_ptr<net::test_server::HttpResponse>(
-        new net::test_server::RawHttpResponse("", ""));
+  static MockedResponse NetworkErrorResponseHandler() {
+    network::mojom::URLResponseHeadPtr head =
+        network::mojom::URLResponseHead::New();
+    return MockedResponse{
+        std::move(head), "",
+        network::URLLoaderCompletionStatus(net::ERR_EMPTY_RESPONSE)};
   }
 
   // Updates the notifier's time with the specified parameters.
@@ -167,8 +200,20 @@
   raw_ptr<base::SimpleTestTickClock> tick_clock_;
   TestingPrefServiceSimple pref_service_;
   std::unique_ptr<NetworkTimeTracker> tracker_;
-  std::unique_ptr<net::EmbeddedTestServer> test_server_;
-  scoped_refptr<network::TestSharedURLLoaderFactory> shared_url_loader_factory_;
+  network::TestURLLoaderFactory url_loader_factory_;
+  base::RepeatingCallback<MockedResponse()> response_handler_;
+
+ private:
+  void Intercept(const network::ResourceRequest& request) {
+    CHECK(response_handler_);
+    MockedResponse response = response_handler_.Run();
+    // status.decoded_body_length = response.body.size();
+    url_loader_factory_.AddResponse(request.url, std::move(response.head),
+                                    std::move(response.body),
+                                    std::move(response.status));
+  }
+
+  base::WeakPtrFactory<NetworkTimeTrackerTest> weak_ptr_factory_{this};
 };
 
 TEST_F(NetworkTimeTrackerTest, Uninitialized) {
@@ -468,18 +513,19 @@
   // First query should happen soon.
   EXPECT_EQ(base::Minutes(0), tracker_->GetTimerDelayForTesting());
 
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
             tracker_->GetNetworkTime(&out_network_time, nullptr));
-  EXPECT_EQ(base::Time::UnixEpoch() +
-                base::Milliseconds((uint64_t)kGoodTimeResponseHandlerJsTime[0]),
-            out_network_time);
+
+  // Enabling load timing for the resource requests seems to increase accuracy
+  // beyond milliseconds. Accuracy of GoodTimeResponseHandler is
+  // milliseconds, any difference below 1 ms can therefore be ignored.
+  EXPECT_LT(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]) -
+                out_network_time,
+            base::Milliseconds(1));
   // Should see no backoff in the success case.
   EXPECT_EQ(base::Minutes(60), tracker_->GetTimerDelayForTesting());
 
@@ -489,10 +535,7 @@
 }
 
 TEST_F(NetworkTimeTrackerTest, StartTimeFetch) {
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   base::Time out_network_time;
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_NO_SYNC_ATTEMPT,
@@ -505,9 +548,12 @@
 
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
             tracker_->GetNetworkTime(&out_network_time, nullptr));
-  EXPECT_EQ(base::Time::UnixEpoch() +
-                base::Milliseconds((uint64_t)kGoodTimeResponseHandlerJsTime[0]),
-            out_network_time);
+  // Enabling load timing for the resource requests seems to increase accuracy
+  // beyond milliseconds. Accuracy of GoodTimeResponseHandler is milliseconds,
+  // any difference below 1 ms can therefore be ignored.
+  EXPECT_LT(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]) -
+                out_network_time,
+            base::Milliseconds(1));
   // Should see no backoff in the success case.
   EXPECT_EQ(base::Minutes(60), tracker_->GetTimerDelayForTesting());
 }
@@ -515,10 +561,7 @@
 // Tests that when StartTimeFetch() is called with a query already in
 // progress, it calls the callback when that query completes.
 TEST_F(NetworkTimeTrackerTest, StartTimeFetchWithQueryInProgress) {
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   base::Time out_network_time;
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_NO_SYNC_ATTEMPT,
@@ -533,9 +576,12 @@
 
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_AVAILABLE,
             tracker_->GetNetworkTime(&out_network_time, nullptr));
-  EXPECT_EQ(base::Time::UnixEpoch() +
-                base::Milliseconds((uint64_t)kGoodTimeResponseHandlerJsTime[0]),
-            out_network_time);
+  // Enabling load timing for the resource requests seems to increase accuracy
+  // beyond milliseconds. Accuracy of GoodTimeResponseHandler is milliseconds,
+  // any difference below 1 ms can therefore be ignored.
+  EXPECT_LT(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]) -
+                out_network_time,
+            base::Milliseconds(1));
   // Should see no backoff in the success case.
   EXPECT_EQ(base::Minutes(60), tracker_->GetTimerDelayForTesting());
 }
@@ -543,10 +589,7 @@
 // Tests that StartTimeFetch() returns false if called while network
 // time is available.
 TEST_F(NetworkTimeTrackerTest, StartTimeFetchWhileSynced) {
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   base::Time in_network_time = clock_->Now();
   UpdateNetworkTime(in_network_time, resolution_, latency_,
@@ -563,10 +606,7 @@
   field_trial_test_->SetFeatureParams(
       true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_ONLY,
       NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   base::Time out_network_time;
   EXPECT_EQ(NetworkTimeTracker::NETWORK_TIME_NO_SYNC_ATTEMPT,
@@ -577,10 +617,7 @@
 }
 
 TEST_F(NetworkTimeTrackerTest, NoNetworkQueryWhileSynced) {
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   field_trial_test_->SetFeatureParams(
       true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
@@ -615,6 +652,7 @@
   field_trial_test_->SetFeatureParams(
       true, 0.0, NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
       NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 }
@@ -624,10 +662,7 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(base::BindRepeating(
-      &NetworkTimeTrackerTest::BadSignatureResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&BadSignatureResponseHandler));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
@@ -656,13 +691,11 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&NetworkTimeTrackerTest::BadDataResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
   base::StringPiece key = {reinterpret_cast<const char*>(kDevKeyPubBytes),
                            sizeof(kDevKeyPubBytes)};
   tracker_->SetPublicKeyForTesting(key);
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
   base::Time out_network_time;
@@ -680,10 +713,8 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&NetworkTimeTrackerTest::ServerErrorResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
@@ -713,10 +744,8 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(base::BindRepeating(
+  SetResponseHandler(base::BindRepeating(
       &NetworkTimeTrackerTest::NetworkErrorResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
@@ -737,10 +766,7 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
-      base::BindRepeating(&GoodTimeResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
+  SetResponseHandler(base::BindRepeating(&GoodTimeResponseHandler));
 
   base::Time out_network_time;
 
@@ -769,13 +795,11 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&NetworkTimeTrackerTest::BadDataResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
   base::StringPiece key = {reinterpret_cast<const char*>(kDevKeyPubBytes),
                            sizeof(kDevKeyPubBytes)};
   tracker_->SetPublicKeyForTesting(key);
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
 
   // Do not wait for the fetch to complete; ask for the network time
@@ -794,13 +818,11 @@
   histograms.ExpectTotalCount(kFetchFailedHistogram, 0);
   histograms.ExpectTotalCount(kFetchValidHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&NetworkTimeTrackerTest::BadDataResponseHandler));
-  EXPECT_TRUE(test_server_->Start());
   base::StringPiece key = {reinterpret_cast<const char*>(kDevKeyPubBytes),
                            sizeof(kDevKeyPubBytes)};
   tracker_->SetPublicKeyForTesting(key);
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
@@ -823,7 +845,8 @@
 namespace {
 
 // NetworkTimeTrackerTest.TimeBetweenFetchesHistogram needs to make several time
-// queries that return different times. MultipleGoodTimeResponseHandler is like
+// queries that return different times.
+// MultipleGoodTimeResponseHandler::ResponseHandler is like
 // GoodTimeResponseHandler, but returning different times on each of three
 // requests that happen in sequence.
 //
@@ -839,8 +862,7 @@
 
   ~MultipleGoodTimeResponseHandler() {}
 
-  std::unique_ptr<net::test_server::HttpResponse> ResponseHandler(
-      const net::test_server::HttpRequest& request);
+  MockedResponse ResponseHandler();
 
   // Returns the time that is returned in the (i-1)'th response handled by
   // ResponseHandler(), or null base::Time() if too many responses have been
@@ -854,24 +876,26 @@
   unsigned int next_time_index_ = 0;
 };
 
-std::unique_ptr<net::test_server::HttpResponse>
-MultipleGoodTimeResponseHandler::ResponseHandler(
-    const net::test_server::HttpRequest& request) {
-  net::test_server::BasicHttpResponse* response =
-      new net::test_server::BasicHttpResponse();
-
+MockedResponse MultipleGoodTimeResponseHandler::ResponseHandler() {
   if (next_time_index_ >= std::size(kGoodTimeResponseBody)) {
-    response->set_code(net::HTTP_BAD_REQUEST);
-    return std::unique_ptr<net::test_server::HttpResponse>(response);
+    return MockedResponse{network::CreateURLResponseHead(net::HTTP_BAD_REQUEST),
+                          ""};
   }
 
-  response->set_code(net::HTTP_OK);
-  response->set_content(kGoodTimeResponseBody[next_time_index_]);
-  response->AddCustomHeader(
+  network::mojom::URLResponseHeadPtr head =
+      network::CreateURLResponseHead(net::HTTP_OK);
+  head->headers->AddHeader(
       "x-cup-server-proof",
       kGoodTimeResponseServerProofHeader[next_time_index_]);
+
+  // Simulate response latency.
+  head->load_timing.send_end = base::TimeTicks::Now();
+  head->load_timing.receive_headers_start =
+      head->load_timing.send_end + kGoodTimeResponseLatency[next_time_index_];
+  MockedResponse response{std::move(head),
+                          kGoodTimeResponseBody[next_time_index_]};
   next_time_index_++;
-  return std::unique_ptr<net::test_server::HttpResponse>(response);
+  return response;
 }
 
 base::Time MultipleGoodTimeResponseHandler::GetTimeAtIndex(unsigned int i) {
@@ -887,11 +911,9 @@
   base::HistogramTester histograms;
   histograms.ExpectTotalCount(kTimeBetweenFetchesHistogram, 0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting());
   tracker_->WaitForFetchForTesting(123123123);
 
@@ -924,58 +946,106 @@
       NetworkTimeTracker::ClockDriftSamples::NO_SAMPLES);
 
   MultipleGoodTimeResponseHandler response_handler;
-  base::HistogramTester histograms;
+  base::HistogramTester histograms_first;
+  base::TimeDelta mean_latency = base::Seconds(0);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
 
   clock_->SetNow(
       base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0] + 3500));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
-  // Simulate 1s latency.
-  tick_clock_->Advance(base::Seconds(1));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency1 = kGoodTimeResponseLatency[0];
+  mean_latency += latency1;
 
-  histograms.ExpectUniqueTimeSample(
-      "PrivacyBudget.ClockSkew.Magnitude.Positive", base::Seconds(3), 1);
-  histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Negative", 0);
-  histograms.ExpectUniqueTimeSample("PrivacyBudget.ClockSkew.FetchLatency",
-                                    base::Seconds(1), 1);
-  histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatencyJitter", 0);
+  std::unique_ptr<base::HistogramSamples> samples_positive(
+      histograms_first.GetHistogramSamplesSinceCreation(
+          "PrivacyBudget.ClockSkew.Magnitude.Positive"));
+  EXPECT_EQ(1, samples_positive->GetCount(
+                   (base::Seconds(3.5) - latency1 / 2).InMilliseconds()));
+  EXPECT_EQ(1, samples_positive->TotalCount());
+
+  std::unique_ptr<base::HistogramSamples> samples_negative(
+      histograms_first.GetHistogramSamplesSinceCreation(
+          "PrivacyBudget.ClockSkew.Magnitude.Negative"));
+  EXPECT_EQ(0, samples_negative->TotalCount());
+
+  std::unique_ptr<base::HistogramSamples> samples_latency(
+      histograms_first.GetHistogramSamplesSinceCreation(
+          "PrivacyBudget.ClockSkew.FetchLatency"));
+  EXPECT_EQ(1, samples_latency->TotalCount());
+  EXPECT_EQ(1, samples_latency->GetCount(latency1.InMilliseconds()));
+
+  std::unique_ptr<base::HistogramSamples> samples_latency_jitter(
+      histograms_first.GetHistogramSamplesSinceCreation(
+          "PrivacyBudget.ClockSkew.FetchLatencyJitter"));
+  EXPECT_EQ(0, samples_latency_jitter->TotalCount());
+
+  base::HistogramTester histograms_second;
 
   clock_->SetNow(
       base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1] + 3500));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
-  // Simulate 1s latency.
-  tick_clock_->Advance(base::Seconds(1));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency2 = kGoodTimeResponseLatency[1];
+  mean_latency += latency2;
 
-  histograms.ExpectUniqueTimeSample(
-      "PrivacyBudget.ClockSkew.Magnitude.Positive", base::Seconds(3), 2);
-  histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.Magnitude.Negative", 0);
-  histograms.ExpectUniqueTimeSample("PrivacyBudget.ClockSkew.FetchLatency",
-                                    base::Seconds(1), 2);
-  histograms.ExpectTotalCount("PrivacyBudget.ClockSkew.FetchLatencyJitter", 0);
+  samples_positive = histograms_second.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.Magnitude.Positive");
+  EXPECT_EQ(1, samples_positive->GetCount(
+                   (base::Seconds(3.5) - latency2 / 2).InMilliseconds()));
+  EXPECT_EQ(1, samples_positive->TotalCount());
 
+  samples_negative = histograms_second.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.Magnitude.Negative");
+  EXPECT_EQ(0, samples_negative->TotalCount());
+
+  samples_latency = histograms_second.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.FetchLatency");
+  EXPECT_EQ(1, samples_latency->TotalCount());
+  EXPECT_EQ(1, samples_latency->GetCount(latency2.InMilliseconds()));
+
+  samples_latency_jitter = histograms_second.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.FetchLatencyJitter");
+  EXPECT_EQ(0, samples_latency_jitter->TotalCount());
+
+  base::HistogramTester histograms_third;
   clock_->SetNow(
       base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[2] - 2500));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
-  // Simulate 1s latency.
-  tick_clock_->Advance(base::Seconds(1));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency3 = kGoodTimeResponseLatency[2];
+  mean_latency += latency3;
 
-  histograms.ExpectUniqueTimeSample(
-      "PrivacyBudget.ClockSkew.Magnitude.Positive", base::Seconds(3), 2);
-  histograms.ExpectUniqueTimeSample(
-      "PrivacyBudget.ClockSkew.Magnitude.Negative", base::Seconds(3), 1);
-  histograms.ExpectUniqueTimeSample("PrivacyBudget.ClockSkew.FetchLatency",
-                                    base::Seconds(1), 3);
+  samples_positive = histograms_third.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.Magnitude.Positive");
+  EXPECT_EQ(0, samples_positive->TotalCount());
+
+  samples_negative = histograms_third.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.Magnitude.Negative");
+  EXPECT_EQ(1, samples_negative->TotalCount());
+  EXPECT_EQ(1, samples_negative->GetCount(
+                   (base::Seconds(2.5) + latency3 / 2).InMilliseconds()));
+  samples_latency = histograms_third.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.FetchLatency");
+  EXPECT_EQ(1, samples_latency->TotalCount());
+  EXPECT_EQ(1, samples_latency->GetCount(latency3.InMilliseconds()));
   // After three fetches, the FetchLatencyJitter should be reported.
-  histograms.ExpectUniqueTimeSample(
-      "PrivacyBudget.ClockSkew.FetchLatencyJitter", base::Seconds(0), 1);
+  samples_latency_jitter = histograms_third.GetHistogramSamplesSinceCreation(
+      "PrivacyBudget.ClockSkew.FetchLatencyJitter");
+  EXPECT_EQ(1, samples_latency_jitter->TotalCount());
+  mean_latency /= 3.0;
+  int64_t stddev = (mean_latency - latency1).InMicroseconds() *
+                       (mean_latency - latency1).InMicroseconds() +
+                   (mean_latency - latency2).InMicroseconds() *
+                       (mean_latency - latency2).InMicroseconds() +
+                   (mean_latency - latency3).InMicroseconds() *
+                       (mean_latency - latency3).InMicroseconds();
+  stddev = std::lround(std::sqrt(base::strict_cast<double>(stddev)));
+  EXPECT_EQ(1, samples_latency_jitter->GetCount(
+                   base::Microseconds(stddev).InMilliseconds()));
 }
 
 TEST_F(NetworkTimeTrackerTest, ClockSkewHistogramsEmptyForOnDemandChecks) {
@@ -986,11 +1056,9 @@
   MultipleGoodTimeResponseHandler response_handler;
   base::HistogramTester histograms;
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
 
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
   tracker_->WaitForFetchForTesting(123123123);
@@ -1010,11 +1078,9 @@
       NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
       NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
   clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[0]));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/true));
   tracker_->WaitForFetchForTesting(123123123);
@@ -1043,11 +1109,9 @@
       NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
       NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
 
   // This part will trigger a skew measurement fetch first, followed by a drift
   // measurement using two samples.
@@ -1058,9 +1122,8 @@
   // The next measurements are used for computing drift.
   clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1]));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
-  // Simulate 70ms latency.
-  tick_clock_->Advance(base::Milliseconds(70));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency1 = kGoodTimeResponseLatency[1];
 
   // We add an on demand time query in the middle to check it does not interfere
   // with our samples.
@@ -1075,21 +1138,28 @@
   clock_->SetNow(
       base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[4] + 150));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
-  // Simulate 50ms latency.
-  tick_clock_->Advance(base::Milliseconds(50));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency3 = kGoodTimeResponseLatency[4];
 
+  double expected_positive_drift =
+      (base::Milliseconds(150) - latency3 / 2 + latency1 / 2).InMicroseconds() /
+      2.0;
+  ASSERT_GT(expected_positive_drift, 0);
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Positive", 1);
-  histograms.ExpectUniqueSample(
-      "PrivacyBudget.ClockDrift.Magnitude.Positive",
-      base::Milliseconds(150 - 50 / 2 + 70 / 2).InMicroseconds() / 2.0, 1);
+  histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.Magnitude.Positive",
+                                expected_positive_drift, 1);
 
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Negative", 0);
 
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.FetchLatencyVariance",
                               1);
+
+  base::TimeDelta mean = (latency1 + latency3) / 2.0;
+  double variance =
+      (latency1 - mean).InMilliseconds() * (latency1 - mean).InMilliseconds() +
+      (latency3 - mean).InMilliseconds() * (latency3 - mean).InMilliseconds();
   histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.FetchLatencyVariance",
-                                200, 1);
+                                variance, 1);
 }
 
 TEST_F(NetworkTimeTrackerTest, ClockDriftHistogramsNegative) {
@@ -1101,11 +1171,9 @@
       NetworkTimeTracker::FETCHES_IN_BACKGROUND_AND_ON_DEMAND,
       NetworkTimeTracker::ClockDriftSamples::TWO_SAMPLES);
 
-  test_server_->RegisterRequestHandler(
+  SetResponseHandler(
       base::BindRepeating(&MultipleGoodTimeResponseHandler::ResponseHandler,
                           base::Unretained(&response_handler)));
-  EXPECT_TRUE(test_server_->Start());
-  tracker_->SetTimeServerURLForTesting(test_server_->GetURL("/"));
 
   // This part will trigger a skew measurement fetch first, followed by a drift
   // measurement using two samples.
@@ -1117,6 +1185,7 @@
   clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[1]));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency1 = kGoodTimeResponseLatency[1];
 
   clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[2]));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
@@ -1125,18 +1194,26 @@
   clock_->SetNow(base::Time::FromJsTime(kGoodTimeResponseHandlerJsTime[3] - 1));
   EXPECT_TRUE(tracker_->QueryTimeServiceForTesting(/*on_demand=*/false));
   tracker_->WaitForFetchForTesting(123123123);
+  base::TimeDelta latency3 = kGoodTimeResponseLatency[3];
 
+  double expected_negative_drift =
+      (base::Milliseconds(1) - latency1 / 2 + latency3 / 2).InMicroseconds() /
+      2.0;
+  ASSERT_GT(expected_negative_drift, 0);
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Positive", 0);
-
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.Magnitude.Negative", 1);
   histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.Magnitude.Negative",
-                                base::Milliseconds(1).InMicroseconds() / 2.0,
-                                1);
+                                expected_negative_drift, 1);
+
+  base::TimeDelta mean = (latency1 + latency3) / 2.0;
+  double variance =
+      (latency1 - mean).InMilliseconds() * (latency1 - mean).InMilliseconds() +
+      (latency3 - mean).InMilliseconds() * (latency3 - mean).InMilliseconds();
 
   histograms.ExpectTotalCount("PrivacyBudget.ClockDrift.FetchLatencyVariance",
                               1);
   histograms.ExpectUniqueSample("PrivacyBudget.ClockDrift.FetchLatencyVariance",
-                                0, 1);
+                                variance, 1);
 }
 
 }  // namespace network_time
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator.h b/components/password_manager/core/browser/password_store_backend_migration_decorator.h
index 0dd6724..8b8b73dd 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator.h
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator.h
@@ -73,7 +73,7 @@
     const raw_ptr<PrefService> prefs_ = nullptr;
 
     // Set when sync_service is already initialized and can be interacted with.
-    raw_ptr<syncer::SyncService> sync_service_ = nullptr;
+    raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
     // Cached value of the configured password sync setting. Updated when the
     // user is changing sync settings, and may from
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.h b/components/password_manager/core/browser/password_store_proxy_backend.h
index 2ce86067..4d9bc13 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.h
+++ b/components/password_manager/core/browser/password_store_proxy_backend.h
@@ -119,7 +119,7 @@
   const raw_ptr<PasswordStoreBackend> android_backend_;
   raw_ptr<PrefService> const prefs_ = nullptr;
   const raw_ptr<SyncDelegate> sync_delegate_;
-  raw_ptr<syncer::SyncService> sync_service_ = nullptr;
+  raw_ptr<const syncer::SyncService> sync_service_ = nullptr;
 
   base::WeakPtrFactory<PasswordStoreProxyBackend> weak_ptr_factory_{this};
 };
diff --git a/components/password_manager/core/browser/password_sync_util.cc b/components/password_manager/core/browser/password_sync_util.cc
index 980a0a3..27efcc3 100644
--- a/components/password_manager/core/browser/password_sync_util.cc
+++ b/components/password_manager/core/browser/password_sync_util.cc
@@ -100,7 +100,7 @@
   return false;
 }
 
-bool IsPasswordSyncEnabled(syncer::SyncService* sync_service) {
+bool IsPasswordSyncEnabled(const syncer::SyncService* sync_service) {
   return sync_service && sync_service->IsSyncFeatureEnabled() &&
          sync_service->GetUserSettings()->GetSelectedTypes().Has(
              syncer::UserSelectableType::kPasswords);
diff --git a/components/password_manager/core/browser/password_sync_util.h b/components/password_manager/core/browser/password_sync_util.h
index 3a83e4b5..267c1e98 100644
--- a/components/password_manager/core/browser/password_sync_util.h
+++ b/components/password_manager/core/browser/password_sync_util.h
@@ -50,7 +50,7 @@
                                       const PrefService& prefs);
 
 // If syncing passwords is enabled in settings.
-bool IsPasswordSyncEnabled(syncer::SyncService* sync_service);
+bool IsPasswordSyncEnabled(const syncer::SyncService* sync_service);
 
 }  // namespace sync_util
 
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 344ec9b3..85439ff 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -156,7 +156,7 @@
 // Enables a revised opt-in flow for the account-scoped password storage.
 const base::Feature kPasswordsAccountStorageRevisedOptInFlow = {
     "PasswordsAccountStorageRevisedOptInFlow",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables password scripts fetching for the |PasswordChangeInSettings| feature.
 const base::Feature kPasswordScriptsFetching = {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index e6aba16..5fc9752 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -24771,7 +24771,7 @@
     },
     {
       'name': 'StartupBrowserWindowLaunchSuppressed',
-      'owners': ['jityao@chromium.org', 'hendrich@chromium.org'],
+      'owners': ['file://components/policy/resources/OWNERS', 'hendrich@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome_os:76-'],
diff --git a/components/services/app_service/public/cpp/intent_util.cc b/components/services/app_service/public/cpp/intent_util.cc
index 62e85c5..b64edaa 100644
--- a/components/services/app_service/public/cpp/intent_util.cc
+++ b/components/services/app_service/public/cpp/intent_util.cc
@@ -91,6 +91,7 @@
 const char kIntentActionSend[] = "send";
 const char kIntentActionSendMultiple[] = "send_multiple";
 const char kIntentActionCreateNote[] = "create_note";
+const char kIntentActionStartOnLockScreen[] = "start_on_lock_screen";
 const char kIntentActionEdit[] = "edit";
 const char kIntentActionPotentialFileHandler[] = "potential_file_handler";
 
@@ -166,10 +167,12 @@
   return intent;
 }
 
-apps::mojom::IntentPtr CreateCreateNoteIntent() {
-  auto intent = apps::mojom::Intent::New();
-  intent->action = kIntentActionCreateNote;
-  return intent;
+apps::IntentPtr CreateCreateNoteIntent() {
+  return std::make_unique<apps::Intent>(kIntentActionCreateNote);
+}
+
+apps::IntentPtr CreateStartOnLockScreenIntent() {
+  return std::make_unique<apps::Intent>(kIntentActionStartOnLockScreen);
 }
 
 apps::mojom::IntentPtr CreateViewIntentFromFiles(
diff --git a/components/services/app_service/public/cpp/intent_util.h b/components/services/app_service/public/cpp/intent_util.h
index 59daf1db..86cad19 100644
--- a/components/services/app_service/public/cpp/intent_util.h
+++ b/components/services/app_service/public/cpp/intent_util.h
@@ -27,6 +27,7 @@
 extern const char kIntentActionSend[];
 extern const char kIntentActionSendMultiple[];
 extern const char kIntentActionCreateNote[];
+extern const char kIntentActionStartOnLockScreen[];
 // A request to edit a file in an app. Must include an attached file.
 extern const char kIntentActionEdit[];
 extern const char kIntentActionPotentialFileHandler[];
@@ -73,7 +74,10 @@
 apps::mojom::IntentPtr CreateIntentFromUrl(const GURL& url);
 
 // Create an intent struct for a Create Note action.
-apps::mojom::IntentPtr CreateCreateNoteIntent();
+apps::IntentPtr CreateCreateNoteIntent();
+
+// Create an intent struct for a "Start On Lock Screen" action.
+apps::IntentPtr CreateStartOnLockScreenIntent();
 
 // Create an intent struct with the list of files with action kIntentActionView.
 apps::mojom::IntentPtr CreateViewIntentFromFiles(
diff --git a/components/sync/driver/trusted_vault_histograms.cc b/components/sync/driver/trusted_vault_histograms.cc
index 6233d94..f34004c 100644
--- a/components/sync/driver/trusted_vault_histograms.cc
+++ b/components/sync/driver/trusted_vault_histograms.cc
@@ -4,10 +4,33 @@
 
 #include "components/sync/driver/trusted_vault_histograms.h"
 
+#include <string>
+
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
 
 namespace syncer {
 
+namespace {
+
+std::string GetReasonSuffix(TrustedVaultURLFetchReasonForUMA reason) {
+  switch (reason) {
+    case TrustedVaultURLFetchReasonForUMA::kUnspecified:
+      return std::string();
+    case TrustedVaultURLFetchReasonForUMA::kRegisterDevice:
+      return "RegisterDevice";
+    case TrustedVaultURLFetchReasonForUMA::
+        kRegisterUnspecifiedAuthenticationFactor:
+      return "RegisterUnspecifiedAuthenticationFactor";
+    case TrustedVaultURLFetchReasonForUMA::kDownloadKeys:
+      return "DownloadKeys";
+    case TrustedVaultURLFetchReasonForUMA::kDownloadIsRecoverabilityDegraded:
+      return "DownloadIsRecoverabilityDegraded";
+  }
+}
+
+}  // namespace
+
 void RecordTrustedVaultDeviceRegistrationState(
     TrustedVaultDeviceRegistrationStateForUMA registration_state) {
   base::UmaHistogramEnumeration("Sync.TrustedVaultDeviceRegistrationState",
@@ -16,13 +39,23 @@
 
 // Records url fetch response status (combined http and net error code).
 // Either |http_status| or |net_error| must be non zero.
-void RecordTrustedVaultURLFetchResponse(int http_response_code, int net_error) {
+void RecordTrustedVaultURLFetchResponse(
+    int http_response_code,
+    int net_error,
+    TrustedVaultURLFetchReasonForUMA reason) {
   DCHECK_LE(net_error, 0);
   DCHECK_GE(http_response_code, 0);
 
-  base::UmaHistogramSparse(
-      "Sync.TrustedVaultURLFetchResponse",
-      http_response_code == 0 ? net_error : http_response_code);
+  const int value = http_response_code == 0 ? net_error : http_response_code;
+  const std::string suffix = GetReasonSuffix(reason);
+
+  base::UmaHistogramSparse("Sync.TrustedVaultURLFetchResponse", value);
+
+  if (!suffix.empty()) {
+    base::UmaHistogramSparse(
+        base::StrCat({"Sync.TrustedVaultURLFetchResponse", ".", suffix}),
+        value);
+  }
 }
 
 }  // namespace syncer
diff --git a/components/sync/driver/trusted_vault_histograms.h b/components/sync/driver/trusted_vault_histograms.h
index b3b5bd6c..63c5dfd 100644
--- a/components/sync/driver/trusted_vault_histograms.h
+++ b/components/sync/driver/trusted_vault_histograms.h
@@ -20,13 +20,26 @@
   kMaxValue = kAttemptingRegistrationWithPersistentAuthError,
 };
 
+// Used to provide UMA metric breakdowns.
+enum class TrustedVaultURLFetchReasonForUMA {
+  kUnspecified,
+  kRegisterDevice,
+  kRegisterUnspecifiedAuthenticationFactor,
+  kDownloadKeys,
+  kDownloadIsRecoverabilityDegraded,
+};
+
 void RecordTrustedVaultDeviceRegistrationState(
     TrustedVaultDeviceRegistrationStateForUMA registration_state);
 
 // Records url fetch response status (combined http and net error code). If
 // |http_response_code| is non-zero, it will be recorded, otherwise |net_error|
 // will be recorded.
-void RecordTrustedVaultURLFetchResponse(int http_response_code, int net_error);
+void RecordTrustedVaultURLFetchResponse(
+    int http_response_code,
+    int net_error,
+    TrustedVaultURLFetchReasonForUMA reason =
+        TrustedVaultURLFetchReasonForUMA::kUnspecified);
 
 }  // namespace syncer
 
diff --git a/components/sync/trusted_vault/trusted_vault_connection_impl.cc b/components/sync/trusted_vault/trusted_vault_connection_impl.cc
index 8f44083..ccb05cd 100644
--- a/components/sync/trusted_vault/trusted_vault_connection_impl.cc
+++ b/components/sync/trusted_vault/trusted_vault_connection_impl.cc
@@ -241,6 +241,21 @@
   std::move(callback).Run(status);
 }
 
+TrustedVaultURLFetchReasonForUMA
+GetURLFetchReasonForUMAForJoinSecurityDomainsRequest(
+    AuthenticationFactorType authentication_factor_type) {
+  switch (authentication_factor_type) {
+    case AuthenticationFactorType::kPhysicalDevice:
+      return TrustedVaultURLFetchReasonForUMA::kRegisterDevice;
+    case AuthenticationFactorType::kUnspecified:
+      return TrustedVaultURLFetchReasonForUMA::
+          kRegisterUnspecifiedAuthenticationFactor;
+  }
+
+  NOTREACHED();
+  return TrustedVaultURLFetchReasonForUMA::kUnspecified;
+}
+
 }  // namespace
 
 TrustedVaultConnectionImpl::TrustedVaultConnectionImpl(
@@ -298,8 +313,8 @@
       GURL(trusted_vault_service_url_.spec() +
            GetGetSecurityDomainMemberURLPathAndQuery(
                device_key_pair->public_key().ExportToBytes())),
-      /*serialized_request_proto=*/absl::nullopt,
-      GetOrCreateURLLoaderFactory());
+      /*serialized_request_proto=*/absl::nullopt, GetOrCreateURLLoaderFactory(),
+      TrustedVaultURLFetchReasonForUMA::kDownloadKeys);
 
   request->FetchAccessTokenAndSendRequest(
       account_info.account_id, access_token_fetcher_.get(),
@@ -321,8 +336,8 @@
       TrustedVaultRequest::HttpMethod::kGet,
       GURL(trusted_vault_service_url_.spec() +
            kGetSecurityDomainURLPathAndQuery),
-      /*serialized_request_proto=*/absl::nullopt,
-      GetOrCreateURLLoaderFactory());
+      /*serialized_request_proto=*/absl::nullopt, GetOrCreateURLLoaderFactory(),
+      TrustedVaultURLFetchReasonForUMA::kDownloadIsRecoverabilityDegraded);
 
   request->FetchAccessTokenAndSendRequest(
       account_info.account_id, access_token_fetcher_.get(),
@@ -350,7 +365,9 @@
           authentication_factor_public_key, authentication_factor_type,
           authentication_factor_type_hint)
           .SerializeAsString(),
-      GetOrCreateURLLoaderFactory());
+      GetOrCreateURLLoaderFactory(),
+      GetURLFetchReasonForUMAForJoinSecurityDomainsRequest(
+          authentication_factor_type));
 
   request->FetchAccessTokenAndSendRequest(
       account_info.account_id, access_token_fetcher_.get(),
diff --git a/components/sync/trusted_vault/trusted_vault_request.cc b/components/sync/trusted_vault/trusted_vault_request.cc
index 34da26b..e397788 100644
--- a/components/sync/trusted_vault/trusted_vault_request.cc
+++ b/components/sync/trusted_vault/trusted_vault_request.cc
@@ -76,11 +76,13 @@
     HttpMethod http_method,
     const GURL& request_url,
     const absl::optional<std::string>& serialized_request_proto,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    TrustedVaultURLFetchReasonForUMA reason_for_uma)
     : http_method_(http_method),
       request_url_(request_url),
       serialized_request_proto_(serialized_request_proto),
-      url_loader_factory_(std::move(url_loader_factory)) {
+      url_loader_factory_(std::move(url_loader_factory)),
+      reason_for_uma_(reason_for_uma) {
   DCHECK(url_loader_factory_);
   DCHECK(http_method == HttpMethod::kPost ||
          !serialized_request_proto.has_value());
@@ -129,7 +131,8 @@
   }
 
   RecordTrustedVaultURLFetchResponse(/*http_response_code=*/http_response_code,
-                                     /*net_error=*/url_loader_->NetError());
+                                     /*net_error=*/url_loader_->NetError(),
+                                     reason_for_uma_);
 
   std::string response_content = response_body ? *response_body : std::string();
   if (http_response_code == net::HTTP_BAD_REQUEST) {
diff --git a/components/sync/trusted_vault/trusted_vault_request.h b/components/sync/trusted_vault/trusted_vault_request.h
index e66f407..ebf56a2 100644
--- a/components/sync/trusted_vault/trusted_vault_request.h
+++ b/components/sync/trusted_vault/trusted_vault_request.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "components/sync/driver/trusted_vault_histograms.h"
 #include "components/sync/trusted_vault/trusted_vault_connection.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -62,7 +63,8 @@
       HttpMethod http_method,
       const GURL& request_url,
       const absl::optional<std::string>& serialized_request_proto,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      TrustedVaultURLFetchReasonForUMA reason_for_uma);
   TrustedVaultRequest(const TrustedVaultRequest& other) = delete;
   TrustedVaultRequest& operator=(const TrustedVaultRequest& other) = delete;
   ~TrustedVaultRequest() override;
@@ -93,6 +95,7 @@
   const GURL request_url_;
   const absl::optional<std::string> serialized_request_proto_;
   const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  const TrustedVaultURLFetchReasonForUMA reason_for_uma_;
 
   CompletionCallback completion_callback_;
 
diff --git a/components/sync/trusted_vault/trusted_vault_request_unittest.cc b/components/sync/trusted_vault/trusted_vault_request_unittest.cc
index 51f9209d..080b6c7 100644
--- a/components/sync/trusted_vault/trusted_vault_request_unittest.cc
+++ b/components/sync/trusted_vault/trusted_vault_request_unittest.cc
@@ -92,7 +92,8 @@
 
     auto request = std::make_unique<TrustedVaultRequest>(
         http_method, GURL(kRequestUrl), request_body,
-        shared_url_loader_factory_);
+        shared_url_loader_factory_,
+        TrustedVaultURLFetchReasonForUMA::kUnspecified);
     request->FetchAccessTokenAndSendRequest(account_id, &access_token_fetcher,
                                             std::move(completion_callback));
     return request;
diff --git a/components/test/data/autofill/automated_integration/testdata/profile_data.py b/components/test/data/autofill/automated_integration/testdata/profile_data.py
index 83cd600..471eb40 100644
--- a/components/test/data/autofill/automated_integration/testdata/profile_data.py
+++ b/components/test/data/autofill/automated_integration/testdata/profile_data.py
@@ -26,13 +26,6 @@
     'ADDRESS_HOME_STATE': 'California',
     'ADDRESS_HOME_ZIP': '94035',
     'ADDRESS_HOME_COUNTRY': 'United States',
-    'ADDRESS_BILLING_LINE1': '314 Oceanwalk Rd.',
-    'ADDRESS_BILLING_LINE2': 'Apt. 4',
-    'ADDRESS_BILLING_APPT_NUM': '4',
-    'ADDRESS_BILLING_CITY': 'Mountain View',
-    'ADDRESS_BILLING_STATE': 'California',
-    'ADDRESS_BILLING_ZIP': '94035',
-    'ADDRESS_BILLING_COUNTRY': 'United States',
     'CREDIT_CARD_NAME': 'Donald Figgleburg',
     'CREDIT_CARD_NUMBER': '5432123498764567',
     'CREDIT_CARD_EXP_MONTH': '05',
@@ -49,27 +42,14 @@
     # mappings for these pages are generated by autocheckout's buildstorage
     # script.
     'FIELD_WITH_DEFAULT_VALUE': '',
-    'PHONE_BILLING_NUMBER': '',
-    'PHONE_BILLING_CITY_CODE': '',
-    'PHONE_BILLING_COUNTRY_CODE': '',
-    'PHONE_BILLING_CITY_AND_NUMBER': '',
-    'PHONE_BILLING_WHOLE_NUMBER': '',
-    'NAME_BILLING_FIRST': '',
-    'NAME_BILLING_MIDDLE': '',
-    'NAME_BILLING_LAST': '',
-    'NAME_BILLING_MIDDLE_INITIAL': '',
-    'NAME_BILLING_FULL': '',
-    'NAME_BILLING_SUFFIX': '',
 
     # Includes of the lines of a street address, including newlines, e.g.
     #   123 Main Street,
     #   Apt. #42
     'ADDRESS_HOME_STREET_ADDRESS': '314 Oceanwalk Rd.\nApt. 4',
-    'ADDRESS_BILLING_STREET_ADDRESS': '314 Oceanwalk Rd.\nApt. 4',
 
     'ACCOUNT_CREATION_PASSWORD': '',
 
     # The third line of the street address.
     'ADDRESS_HOME_LINE3': '',
-    'ADDRESS_BILLING_LINE3': ''
 }
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 915e5c6..19b8155 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -304,6 +304,7 @@
   FF_NAVIGATION_INVALID_URL = 277,
   FTN_ANONYMOUS = 278,
   BFSI_CREATE_FOR_WORKER_FENCED_FRAME = 279,
+  PMM_SUBSCRIBE_IN_FENCED_FRAME = 280,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index b1afa3e0..32a6df4 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -424,23 +424,23 @@
 }
 
 void StorageHandler::GetUsageAndQuota(
-    const String& origin,
+    const String& origin_string,
     std::unique_ptr<GetUsageAndQuotaCallback> callback) {
   if (!storage_partition_)
     return callback->sendFailure(Response::InternalError());
 
-  GURL origin_url(origin);
-  if (!origin_url.is_valid()) {
+  GURL origin_url(origin_string);
+  url::Origin origin = url::Origin::Create(origin_url);
+  if (!origin_url.is_valid() || origin.opaque()) {
     return callback->sendFailure(
-        Response::ServerError(origin + " is not a valid URL"));
+        Response::ServerError(origin_string + " is not a valid URL"));
   }
 
   storage::QuotaManager* manager = storage_partition_->GetQuotaManager();
   GetIOThreadTaskRunner({})->PostTask(
       FROM_HERE,
       base::BindOnce(&GetUsageAndQuotaOnIOThread, base::RetainedRef(manager),
-                     blink::StorageKey(url::Origin::Create(origin_url)),
-                     std::move(callback)));
+                     blink::StorageKey(origin), std::move(callback)));
 }
 
 void StorageHandler::OverrideQuotaForOrigin(
@@ -476,62 +476,65 @@
 
 // TODO(https://crbug.com/1199077): We should think about how this function
 // should be exposed when migrating to storage keys.
-Response StorageHandler::TrackCacheStorageForOrigin(const std::string& origin) {
+Response StorageHandler::TrackCacheStorageForOrigin(
+    const std::string& origin_string) {
   if (!storage_partition_)
     return Response::InternalError();
 
-  GURL origin_url(origin);
-  if (!origin_url.is_valid())
-    return Response::InvalidParams(origin + " is not a valid URL");
+  GURL origin_url(origin_string);
+  url::Origin origin = url::Origin::Create(origin_url);
+  if (!origin_url.is_valid() || origin.opaque())
+    return Response::InvalidParams(origin_string + " is not a valid URL");
 
-  GetCacheStorageObserver()->TrackStorageKey(
-      blink::StorageKey(url::Origin::Create(origin_url)));
+  GetCacheStorageObserver()->TrackStorageKey(blink::StorageKey(origin));
   return Response::Success();
 }
 
 // TODO(https://crbug.com/1199077): We should think about how this function
 // should be exposed when migrating to storage keys.
 Response StorageHandler::UntrackCacheStorageForOrigin(
-    const std::string& origin) {
+    const std::string& origin_string) {
   if (!storage_partition_)
     return Response::InternalError();
 
-  GURL origin_url(origin);
-  if (!origin_url.is_valid())
-    return Response::InvalidParams(origin + " is not a valid URL");
+  GURL origin_url(origin_string);
+  url::Origin origin = url::Origin::Create(origin_url);
+  if (!origin_url.is_valid() || origin.opaque())
+    return Response::InvalidParams(origin_string + " is not a valid URL");
 
-  GetCacheStorageObserver()->UntrackStorageKey(
-      blink::StorageKey(url::Origin::Create(origin_url)));
+  GetCacheStorageObserver()->UntrackStorageKey(blink::StorageKey(origin));
   return Response::Success();
 }
 
-Response StorageHandler::TrackIndexedDBForOrigin(const std::string& origin) {
+Response StorageHandler::TrackIndexedDBForOrigin(
+    const std::string& origin_string) {
   if (!storage_partition_)
     return Response::InternalError();
 
-  GURL origin_url(origin);
-  if (!origin_url.is_valid())
-    return Response::InvalidParams(origin + " is not a valid URL");
+  GURL origin_url(origin_string);
+  url::Origin origin = url::Origin::Create(origin_url);
+  if (!origin_url.is_valid() || origin.opaque())
+    return Response::InvalidParams(origin_string + " is not a valid URL");
 
   // TODO(https://crbug.com/1199077): Pass the real StorageKey into this
   // function once the Chrome DevTools Protocol (CDP) supports StorageKey.
-  GetIndexedDBObserver()->TrackOrigin(
-      blink::StorageKey(url::Origin::Create(origin_url)));
+  GetIndexedDBObserver()->TrackOrigin(blink::StorageKey(origin));
   return Response::Success();
 }
 
-Response StorageHandler::UntrackIndexedDBForOrigin(const std::string& origin) {
+Response StorageHandler::UntrackIndexedDBForOrigin(
+    const std::string& origin_string) {
   if (!storage_partition_)
     return Response::InternalError();
 
-  GURL origin_url(origin);
-  if (!origin_url.is_valid())
-    return Response::InvalidParams(origin + " is not a valid URL");
+  GURL origin_url(origin_string);
+  url::Origin origin = url::Origin::Create(origin_url);
+  if (!origin_url.is_valid() || origin.opaque())
+    return Response::InvalidParams(origin_string + " is not a valid URL");
 
   // TODO(https://crbug.com/1199077): Pass the real StorageKey into this
   // function once the Chrome DevTools Protocol (CDP) supports StorageKey.
-  GetIndexedDBObserver()->UntrackOrigin(
-      blink::StorageKey(url::Origin::Create(origin_url)));
+  GetIndexedDBObserver()->UntrackOrigin(blink::StorageKey(origin));
   return Response::Success();
 }
 
diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc
index 0c59d3c..c969a5d 100644
--- a/content/browser/notifications/blink_notification_service_impl.cc
+++ b/content/browser/notifications/blink_notification_service_impl.cc
@@ -242,6 +242,20 @@
     const blink::NotificationResources& notification_resources,
     DisplayPersistentNotificationCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // The renderer should have checked and disallowed the request for fenced
+  // frames and thrown an error in
+  // blink::ServiceWorkerRegistrationNotifications. Report a bad message if the
+  // renderer if the renderer side check didn't happen for some reason.
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      service_worker_context_->GetLiveRegistration(
+          service_worker_registration_id);
+  if (registration && registration->ancestor_frame_type() ==
+                          blink::mojom::AncestorFrameType::kFencedFrame) {
+    mojo::ReportBadMessage("Notification is not allowed in a fenced frame");
+    return;
+  }
+
   if (!ValidateNotificationDataAndResources(platform_notification_data,
                                             notification_resources))
     return;
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 74ad8c3e4..afe2808 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -193,6 +193,16 @@
     return;
   }
 
+  // The renderer should have checked and disallowed the request for fenced
+  // frames and thrown an exception in blink::PushManager. Report a bad message
+  // if the renderer if the renderer side check didn't happen for some reason.
+  if (service_worker_registration->ancestor_frame_type() ==
+      blink::mojom::AncestorFrameType::kFencedFrame) {
+    bad_message::ReceivedBadMessage(render_process_host_.GetID(),
+                                    bad_message::PMM_SUBSCRIBE_IN_FENCED_FRAME);
+    return;
+  }
+
   const url::Origin& origin = service_worker_registration->key().origin();
 
   if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanAccessDataForOrigin(
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 8ea7d28..dd09276 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -5736,6 +5736,9 @@
 void RenderFrameHostImpl::RemoveDocumentService(
     internal::DocumentServiceBase* document_service,
     base::PassKey<internal::DocumentServiceBase>) {
+  if (document_service == last_web_bluetooth_service_for_testing_) {
+    last_web_bluetooth_service_for_testing_ = nullptr;
+  }
   base::Erase(document_associated_data_->services, document_service);
 }
 
@@ -10045,14 +10048,6 @@
   if (!document_associated_data_ || !last_web_bluetooth_service_for_testing_)
     return nullptr;
 
-  // Checking the list of services to make sure the last WebBluetoothServiceImpl
-  // instance has not been invalidated since then.
-  auto it = base::ranges::find(document_associated_data_->services,
-                               last_web_bluetooth_service_for_testing_);
-  if (document_associated_data_->services.end() == it) {
-    last_web_bluetooth_service_for_testing_ = nullptr;
-  }
-
   return last_web_bluetooth_service_for_testing_;
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index d603c83f..fa26155 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -4235,8 +4235,8 @@
   base::OnceClosure did_stop_loading_callback_;
 
   // Used when testing to retrieve that last created Web Bluetooth service.
-  raw_ptr<WebBluetoothServiceImpl, DanglingUntriaged>
-      last_web_bluetooth_service_for_testing_ = nullptr;
+  raw_ptr<WebBluetoothServiceImpl> last_web_bluetooth_service_for_testing_ =
+      nullptr;
 
   BackForwardCacheDisablingFeaturesCallback
       back_forward_cache_disabling_features_callback_for_testing_;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index e6ceafd..8021128c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -885,10 +885,8 @@
 
 // Enable the web lockscreen API implementation
 // (https://github.com/WICG/lock-screen) in Chrome.
-#if BUILDFLAG(IS_CHROMEOS)
 const base::Feature kWebLockScreenApi{"WebLockScreenApi",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
 
 // Controls whether to isolate sites of documents that specify an eligible
 // Cross-Origin-Opener-Policy header.  Note that this is only intended to be
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 02e0f90e..fc1d35d 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -215,9 +215,7 @@
 CONTENT_EXPORT extern const base::Feature
     kSkipEarlyCommitPendingForCrashedFrame;
 CONTENT_EXPORT extern const base::Feature kUserMediaCaptureOnFocus;
-#if BUILDFLAG(IS_CHROMEOS)
 CONTENT_EXPORT extern const base::Feature kWebLockScreenApi;
-#endif  // BUILDFLAG(IS_CHROMEOS)
 CONTENT_EXPORT extern const base::Feature kWebOTP;
 CONTENT_EXPORT extern const base::Feature kWebOTPAssertionFeaturePolicy;
 CONTENT_EXPORT extern const base::Feature kSpareRendererForSitePerProcess;
diff --git a/extensions/browser/extension_event_histogram_value.h b/extensions/browser/extension_event_histogram_value.h
index 3e6b0db..cce873c 100644
--- a/extensions/browser/extension_event_histogram_value.h
+++ b/extensions/browser/extension_event_histogram_value.h
@@ -515,6 +515,7 @@
   LOGIN_ON_EXTERNAL_LOGOUT_DONE = 493,
   ACCESSIBILITY_PRIVATE_ON_PUMPKIN_INSTALLED = 494,
   ENTERPRISE_REMOTE_APPS_ON_REMOTE_APP_LAUNCHED = 495,
+  INPUT_METHOD_PRIVATE_ON_CARET_BOUNDS_CHANGED = 496,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/updater/extension_downloader.cc b/extensions/browser/updater/extension_downloader.cc
index 2a2569b..fe015dc 100644
--- a/extensions/browser/updater/extension_downloader.cc
+++ b/extensions/browser/updater/extension_downloader.cc
@@ -325,6 +325,12 @@
   ReportStats();
   url_stats_ = URLStats();
 
+  if (g_test_delegate) {
+    g_test_delegate->StartUpdateCheck(this, delegate_,
+                                      std::move(pending_tasks_));
+    pending_tasks_.clear();
+    return;
+  }
   // We limit the number of extensions grouped together in one batch to avoid
   // running into the limits on the length of http GET requests, so there might
   // be multiple ManifestFetchData* objects with the same update_url.
@@ -443,10 +449,6 @@
 
 void ExtensionDownloader::StartUpdateCheck(
     std::unique_ptr<ManifestFetchData> fetch_data) {
-  if (g_test_delegate) {
-    g_test_delegate->StartUpdateCheck(this, delegate_, std::move(fetch_data));
-    return;
-  }
   const ExtensionIdSet extension_ids = fetch_data->GetExtensionIds();
   if (!ExtensionsBrowserClient::Get()->IsBackgroundUpdateAllowed()) {
     NotifyExtensionsDownloadStageChanged(
diff --git a/extensions/browser/updater/extension_downloader_test_delegate.h b/extensions/browser/updater/extension_downloader_test_delegate.h
index 64714d6f..9644e10 100644
--- a/extensions/browser/updater/extension_downloader_test_delegate.h
+++ b/extensions/browser/updater/extension_downloader_test_delegate.h
@@ -5,13 +5,14 @@
 #ifndef EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_TEST_DELEGATE_H_
 #define EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_TEST_DELEGATE_H_
 
-#include <memory>
+#include <vector>
+
+#include "extensions/browser/updater/extension_downloader_task.h"
 
 namespace extensions {
 
 class ExtensionDownloader;
 class ExtensionDownloaderDelegate;
-class ManifestFetchData;
 
 // A class for intercepting the work of checking for / downloading extension
 // updates.
@@ -21,10 +22,9 @@
   // extension. Normally implementors should eventually call either
   // OnExtensionDownloadFailed or OnExtensionDownloadFinished on
   // |delegate|.
-  virtual void StartUpdateCheck(
-      ExtensionDownloader* downloader,
-      ExtensionDownloaderDelegate* delegate,
-      std::unique_ptr<ManifestFetchData> fetch_data) = 0;
+  virtual void StartUpdateCheck(ExtensionDownloader* downloader,
+                                ExtensionDownloaderDelegate* delegate,
+                                std::vector<ExtensionDownloaderTask> tasks) = 0;
 };
 
 }  // namespace extensions
diff --git a/infra/config/chops-weetbix-dev.cfg b/infra/config/chops-weetbix-dev.cfg
index a1205f9..2ce5db0 100644
--- a/infra/config/chops-weetbix-dev.cfg
+++ b/infra/config/chops-weetbix-dev.cfg
@@ -1,14 +1,13 @@
 # Schema for this config file: ProjectConfig in:
 # https://luci-config.appspot.com/schemas/projects:chops-weetbix.cfg
 
-project_metadata {
-  display_name: "Chromium"
-}
-
 bug_filing_threshold {
   presubmit_runs_failed {
     one_day: 3
   }
+  critical_failures_exonerated {
+    three_day: 1
+  }
 }
 
 clustering {
@@ -67,22 +66,15 @@
   priorities {
     priority: "2"
     threshold {
-      presubmit_runs_failed {
-        one_day: 2
-      }
-    }
-  }
-  priorities {
-    priority: "3"
-    threshold {
       # Clusters which fail to meet this threshold will be closed.
-      test_results_failed {
-        one_day: 2
-      }
       presubmit_runs_failed {
         one_day: 1
         seven_day: 1
       }
+      critical_failures_exonerated {
+        three_day: 1
+        seven_day: 1
+      }
     }
   }
   priority_hysteresis_percent: 50
diff --git a/infra/config/chops-weetbix.cfg b/infra/config/chops-weetbix.cfg
index e18ab06..8e09fd8c9 100644
--- a/infra/config/chops-weetbix.cfg
+++ b/infra/config/chops-weetbix.cfg
@@ -5,6 +5,9 @@
   presubmit_runs_failed {
     one_day: 3
   }
+  critical_failures_exonerated {
+    three_day: 1
+  }
 }
 
 clustering {
@@ -63,22 +66,15 @@
   priorities {
     priority: "2"
     threshold {
-      presubmit_runs_failed {
-        one_day: 2
-      }
-    }
-  }
-  priorities {
-    priority: "3"
-    threshold {
       # Clusters which fail to meet this threshold will be closed.
-      test_results_failed {
-        one_day: 2
-      }
       presubmit_runs_failed {
         one_day: 1
         seven_day: 1
       }
+      critical_failures_exonerated {
+        three_day: 1
+        seven_day: 1
+      }
     }
   }
   priority_hysteresis_percent: 50
diff --git a/infra/config/generated/luci/chops-weetbix-dev.cfg b/infra/config/generated/luci/chops-weetbix-dev.cfg
index a1205f9..2ce5db0 100644
--- a/infra/config/generated/luci/chops-weetbix-dev.cfg
+++ b/infra/config/generated/luci/chops-weetbix-dev.cfg
@@ -1,14 +1,13 @@
 # Schema for this config file: ProjectConfig in:
 # https://luci-config.appspot.com/schemas/projects:chops-weetbix.cfg
 
-project_metadata {
-  display_name: "Chromium"
-}
-
 bug_filing_threshold {
   presubmit_runs_failed {
     one_day: 3
   }
+  critical_failures_exonerated {
+    three_day: 1
+  }
 }
 
 clustering {
@@ -67,22 +66,15 @@
   priorities {
     priority: "2"
     threshold {
-      presubmit_runs_failed {
-        one_day: 2
-      }
-    }
-  }
-  priorities {
-    priority: "3"
-    threshold {
       # Clusters which fail to meet this threshold will be closed.
-      test_results_failed {
-        one_day: 2
-      }
       presubmit_runs_failed {
         one_day: 1
         seven_day: 1
       }
+      critical_failures_exonerated {
+        three_day: 1
+        seven_day: 1
+      }
     }
   }
   priority_hysteresis_percent: 50
diff --git a/infra/config/generated/luci/chops-weetbix.cfg b/infra/config/generated/luci/chops-weetbix.cfg
index e18ab06..8e09fd8c9 100644
--- a/infra/config/generated/luci/chops-weetbix.cfg
+++ b/infra/config/generated/luci/chops-weetbix.cfg
@@ -5,6 +5,9 @@
   presubmit_runs_failed {
     one_day: 3
   }
+  critical_failures_exonerated {
+    three_day: 1
+  }
 }
 
 clustering {
@@ -63,22 +66,15 @@
   priorities {
     priority: "2"
     threshold {
-      presubmit_runs_failed {
-        one_day: 2
-      }
-    }
-  }
-  priorities {
-    priority: "3"
-    threshold {
       # Clusters which fail to meet this threshold will be closed.
-      test_results_failed {
-        one_day: 2
-      }
       presubmit_runs_failed {
         one_day: 1
         seven_day: 1
       }
+      critical_failures_exonerated {
+        three_day: 1
+        seven_day: 1
+      }
     }
   }
   priority_hysteresis_percent: 50
diff --git a/ios/build/tools/convert_gn_xcodeproj.py b/ios/build/tools/convert_gn_xcodeproj.py
index 603d343..e0e490c9 100755
--- a/ios/build/tools/convert_gn_xcodeproj.py
+++ b/ios/build/tools/convert_gn_xcodeproj.py
@@ -18,6 +18,7 @@
 import filecmp
 import functools
 import hashlib
+import io
 import json
 import os
 import re
@@ -55,7 +56,7 @@
 @functools.lru_cache
 def LoadSchemeTemplate(root, name):
   """Return a string.Template object for scheme file loaded relative to root."""
-  path = os.path.join(root, 'ios', 'build', 'tools', name)
+  path = os.path.join(root, 'ios', 'build', 'tools', name + '.template')
   with open(path) as file:
     return Template(file.read())
 
@@ -65,7 +66,7 @@
   return hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper()
 
 
-def GenerateSchemeForTarget(root, project, old_project, name, path, tests):
+def GenerateSchemeForTarget(root, project, old_project, name, path, is_test):
   """Generates the .xcsheme file for target named |name|.
 
   The file is generated in the new project schemes directory from a template.
@@ -81,9 +82,23 @@
   if not os.path.isdir(os.path.dirname(scheme_path)):
     os.makedirs(os.path.dirname(scheme_path))
 
+  substitutions = {
+    'LLDBINIT_PATH': LLDBINIT_PATH,
+    'BLUEPRINT_IDENTIFIER': identifier,
+    'BUILDABLE_NAME': path,
+    'BLUEPRINT_NAME': name,
+    'PROJECT_NAME': project_name
+  }
+
+  if is_test:
+    template = LoadSchemeTemplate(root, 'xcodescheme-testable')
+    substitutions['PATH'] = os.environ['PATH']
+
+  else:
+    template = LoadSchemeTemplate(root, 'xcodescheme')
+
   old_scheme_path = os.path.join(old_project, relative_path)
   if os.path.exists(old_scheme_path):
-    made_changes = False
 
     tree = xml.etree.ElementTree.parse(old_scheme_path)
     tree_root = tree.getroot()
@@ -95,7 +110,6 @@
           ('BlueprintIdentifier', identifier)):
         if reference.get(attr) != value:
           reference.set(attr, value)
-          made_changes = True
 
     for child in tree_root:
       if child.tag not in ('TestAction', 'LaunchAction'):
@@ -103,59 +117,29 @@
 
       if child.get('customLLDBInitFile') != LLDBINIT_PATH:
         child.set('customLLDBInitFile', LLDBINIT_PATH)
-        made_changes = True
 
-      # Override the list of testables.
-      if child.tag == 'TestAction':
-        for subchild in child:
-          if subchild.tag != 'Testables':
-            continue
+    if is_test:
 
-          for elt in list(subchild):
-            subchild.remove(elt)
+      template_tree = xml.etree.ElementTree.parse(
+          io.StringIO(template.substitute(**substitutions)))
 
-          if tests:
-            template = LoadSchemeTemplate(root, 'xcodescheme-testable.template')
-            for (key, test_path, test_name) in sorted(tests):
-              testable = ''.join(template.substitute(
-                  BLUEPRINT_IDENTIFIER=key,
-                  BUILDABLE_NAME=test_path,
-                  BLUEPRINT_NAME=test_name,
-                  PROJECT_NAME=project_name))
+      template_tree_root = template_tree.getroot()
+      for child in tree_root:
+        if child.tag != 'BuildAction':
+          continue
 
-              testable_elt = xml.etree.ElementTree.fromstring(testable)
-              subchild.append(testable_elt)
+        for subchild in list(child):
+          child.remove(subchild)
 
-    if made_changes:
-      tree.write(scheme_path, xml_declaration=True, encoding='UTF-8')
+        for post_action in template_tree_root.findall('.//PostActions'):
+          child.append(post_action)
 
-    else:
-      shutil.copyfile(old_scheme_path, scheme_path)
+    tree.write(scheme_path, xml_declaration=True, encoding='UTF-8')
 
   else:
 
-    testables = ''
-    if tests:
-      template = LoadSchemeTemplate(root, 'xcodescheme-testable.template')
-      testables = '\n' + ''.join(
-          template.substitute(
-              BLUEPRINT_IDENTIFIER=key,
-              BUILDABLE_NAME=test_path,
-              BLUEPRINT_NAME=test_name,
-              PROJECT_NAME=project_name)
-          for (key, test_path, test_name) in sorted(tests)).rstrip()
-
-    template = LoadSchemeTemplate(root, 'xcodescheme.template')
-
     with open(scheme_path, 'w') as scheme_file:
-      scheme_file.write(
-          template.substitute(
-              TESTABLES=testables,
-              LLDBINIT_PATH=LLDBINIT_PATH,
-              BLUEPRINT_IDENTIFIER=identifier,
-              BUILDABLE_NAME=path,
-              BLUEPRINT_NAME=name,
-              PROJECT_NAME=project_name))
+      scheme_file.write(template.substitute(**substitutions))
 
 
 class XcodeProject(object):
@@ -342,14 +326,19 @@
     product = project.objects[obj['productReference']]
     product_path = product['path']
 
-    # For XCTests, the key is the product path, while for XCUITests, the key
-    # is the target name. Use a sum of both possible keys (there should not
-    # be overlaps since different hosts are used for XCTests and XCUITests
-    # but this make the code simpler).
+    # Do not generate scheme for the XCTests and XXCUITests target app.
+    # Instead, a scheme will be generated for each test modules.
     tests = mapping.get(product_path, []) + mapping.get(obj['name'], [])
-    GenerateSchemeForTarget(
-        root_dir, project_dir, old_project_dir,
-        obj['name'], product_path, tests)
+    if not tests:
+      GenerateSchemeForTarget(
+          root_dir, project_dir, old_project_dir,
+          obj['name'], product_path, False)
+
+    else:
+      for (_, test_name, test_path) in tests:
+        GenerateSchemeForTarget(
+          root_dir, project_dir, old_project_dir,
+          test_name, test_path, True)
 
   root_object = project.objects[json_data['rootObject']]
   main_group = project.objects[root_object['mainGroup']]
diff --git a/ios/build/tools/xcodescheme-testable.template b/ios/build/tools/xcodescheme-testable.template
index 61b6f47..24ba39a 100644
--- a/ios/build/tools/xcodescheme-testable.template
+++ b/ios/build/tools/xcodescheme-testable.template
@@ -1,3 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1220"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <PostActions>
+         <ExecutionAction
+            ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
+            <ActionContent
+               title = "Resign test runner"
+               scriptText = "unset -v XCODE_DEVELOPER_DIR_PATH&#10;&#10;BUILDDIR=&quot;${PROJECT_DIR}/../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}&quot;&#10;&#10;export PATH=&quot;@{PATH}&quot;&#10;&#10;rm -rf &quot;${BUILDDIR}/@{BLUEPRINT_NAME}-Runner.app&quot;&#10;autoninja -C &quot;${BUILDDIR}&quot; &quot;@{BLUEPRINT_NAME}&quot;&#10;"
+               shellToInvoke = "/bin/sh">
+               <EnvironmentBuildable>
+                  <BuildableReference
+                     BuildableIdentifier = "primary"
+                     BlueprintIdentifier = "@{BLUEPRINT_IDENTIFIER}"
+                     BuildableName = "@{BUILDABLE_NAME}"
+                     BlueprintName = "@{BLUEPRINT_NAME}"
+                     ReferencedContainer = "container:@{PROJECT_NAME}">
+                  </BuildableReference>
+               </EnvironmentBuildable>
+            </ActionContent>
+         </ExecutionAction>
+      </PostActions>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      customLLDBInitFile = "@{LLDBINIT_PATH}"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
          <TestableReference
             skipped = "NO">
             <BuildableReference
@@ -8,3 +42,32 @@
                ReferencedContainer = "container:@{PROJECT_NAME}">
             </BuildableReference>
          </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      customLLDBInitFile = "@{LLDBINIT_PATH}"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Profile"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Official"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/ios/build/tools/xcodescheme.template b/ios/build/tools/xcodescheme.template
index 514bea4e..b7cda4b 100644
--- a/ios/build/tools/xcodescheme.template
+++ b/ios/build/tools/xcodescheme.template
@@ -28,8 +28,6 @@
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       customLLDBInitFile = "@{LLDBINIT_PATH}"
       shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>@{TESTABLES}
-      </Testables>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
diff --git a/ios/chrome/browser/application_context_impl.mm b/ios/chrome/browser/application_context_impl.mm
index 370bff7..a0f39248 100644
--- a/ios/chrome/browser/application_context_impl.mm
+++ b/ios/chrome/browser/application_context_impl.mm
@@ -170,7 +170,7 @@
   // IO thread will handle that URLFetcher operation before going away.)
   metrics::MetricsService* metrics_service = GetMetricsService();
   if (metrics_service)
-    metrics_service->RecordCompletedSessionEnd();
+    metrics_service->LogCleanShutdown();
   metrics_services_manager_.reset();
   network_time_tracker_.reset();
 
diff --git a/ios/chrome/browser/ui/autofill/autofill_ui_type_util.mm b/ios/chrome/browser/ui/autofill/autofill_ui_type_util.mm
index ab7f629..46c4ac4 100644
--- a/ios/chrome/browser/ui/autofill/autofill_ui_type_util.mm
+++ b/ios/chrome/browser/ui/autofill/autofill_ui_type_util.mm
@@ -93,7 +93,7 @@
     case AutofillUITypeProfileHomeAddressZip:
       return autofill::ADDRESS_HOME_ZIP;
     case AutofillUITypeProfileHomeAddressSortingCode:
-      return autofill::ADDRESS_BILLING_SORTING_CODE;
+      return autofill::ADDRESS_HOME_SORTING_CODE;
     case AutofillUITypeProfileHomeAddressCountry:
       return autofill::ADDRESS_HOME_COUNTRY;
     case AutofillUITypeProfileHomePhoneWholeNumber:
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
index c3af9de..77382ca 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.mm
@@ -694,8 +694,8 @@
 - (void)editNode:(const BookmarkNode*)node {
   if (!self.bookmarkInteractionController) {
     self.bookmarkInteractionController =
-        [[BookmarkInteractionController alloc] initWithBrowser:self.browser
-                                              parentController:self];
+        [[BookmarkInteractionController alloc] initWithBrowser:self.browser];
+    self.bookmarkInteractionController.parentController = self;
     self.bookmarkInteractionController.delegate = self;
   }
 
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h
index 27e359a..a4ecee2 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h
@@ -30,9 +30,10 @@
 // This object's delegate.
 @property(nonatomic, weak) id<BookmarkInteractionControllerDelegate> delegate;
 
-- (instancetype)initWithBrowser:(Browser*)browser
-               parentController:(UIViewController*)parentController
-    NS_DESIGNATED_INITIALIZER;
+// The parent controller on top of which the UI needs to be presented.
+@property(nonatomic, weak) UIViewController* parentController;
+
+- (instancetype)initWithBrowser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
 - (instancetype)init NS_UNAVAILABLE;
 
 // Called before the instance is deallocated.
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
index 3df696f..9fed617 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.mm
@@ -86,9 +86,6 @@
   // it is incognito.
   ChromeBrowserState* _browserState;  // weak
 
-  // The parent controller on top of which the UI needs to be presented.
-  __weak UIViewController* _parentController;
-
   // The web state list currently in use.
   WebStateList* _webStateList;
 }
@@ -170,8 +167,7 @@
 @synthesize folderEditor = _folderEditor;
 @synthesize mediator = _mediator;
 
-- (instancetype)initWithBrowser:(Browser*)browser
-               parentController:(UIViewController*)parentController {
+- (instancetype)initWithBrowser:(Browser*)browser {
   self = [super init];
   if (self) {
     _browser = browser;
@@ -179,7 +175,6 @@
     // incognito mode.
     _currentBrowserState = browser->GetBrowserState();
     _browserState = _currentBrowserState->GetOriginalChromeBrowserState();
-    _parentController = parentController;
     // TODO(crbug.com/1045047): Use HandlerForProtocol after commands protocol
     // clean up.
     _handler = static_cast<id<ApplicationCommands, BrowserCommands>>(
@@ -190,7 +185,6 @@
     _mediator = [[BookmarkMediator alloc] initWithBrowserState:_browserState];
     _currentPresentedState = PresentedState::NONE;
     DCHECK(_bookmarkModel);
-    DCHECK(_parentController);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index dc88192f..9d918508 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -264,6 +264,7 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/activity_services",
     "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/chrome/browser/ui/bookmarks",
     "//ios/chrome/browser/ui/browser_container:ui",
     "//ios/chrome/browser/ui/bubble",
     "//ios/chrome/browser/ui/commands",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index fcb70031..4f71841 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -44,6 +44,7 @@
 #import "ios/chrome/browser/ui/authentication/enterprise/enterprise_prompt/enterprise_prompt_type.h"
 #import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.h"
 #import "ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_coordinator.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_view_controller.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller+delegates.h"
@@ -334,6 +335,7 @@
   SideSwipeController* _sideSwipeController;
   // The coordinator that shows the Send Tab To Self UI.
   SendTabToSelfCoordinator* _sendTabToSelfCoordinator;
+  BookmarkInteractionController* _bookmarkInteractionController;
 }
 
 #pragma mark - ChromeCoordinator
@@ -346,6 +348,7 @@
 
   [self createViewControllerDependencies];
   [self createViewController];
+  [self updateViewControllerDependencies];
   // Mediators should start before coordinators so model state is accurate for
   // any UI that starts up.
   [self startMediators];
@@ -569,6 +572,9 @@
     }
   }
 
+  _bookmarkInteractionController =
+      [[BookmarkInteractionController alloc] initWithBrowser:self.browser];
+
   self.browserContainerCoordinator = [[BrowserContainerCoordinator alloc]
       initWithBaseViewController:nil
                          browser:self.browser];
@@ -594,9 +600,12 @@
   _viewControllerDependencies.legacyTabStripCoordinator =
       _legacyTabStripCoordinator;
   _viewControllerDependencies.sideSwipeController = _sideSwipeController;
+  _viewControllerDependencies.bookmarkInteractionController =
+      _bookmarkInteractionController;
 }
 
 - (void)updateViewControllerDependencies {
+  _bookmarkInteractionController.parentController = self.viewController;
 }
 
 // Destroys the browser view controller dependencies.
@@ -611,6 +620,10 @@
   _viewControllerDependencies.tabStripCoordinator = nil;
   _viewControllerDependencies.legacyTabStripCoordinator = nil;
   _viewControllerDependencies.sideSwipeController = nil;
+  _viewControllerDependencies.bookmarkInteractionController = nil;
+
+  [_bookmarkInteractionController shutdown];
+  _bookmarkInteractionController = nil;
 
   _legacyTabStripCoordinator = nil;
   _tabStripCoordinator = nil;
@@ -961,6 +974,10 @@
   [self.readingListCoordinator start];
 }
 
+- (void)showBookmarksManager {
+  [_bookmarkInteractionController presentBookmarks];
+}
+
 - (void)showReadingListIPH {
   [_bubblePresenter presentReadingListBottomToolbarTipBubble];
 }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
index 2e7969321..d59ce9c28 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator_unittest.mm
@@ -5,6 +5,8 @@
 #import "ios/chrome/browser/ui/browser_view/browser_coordinator.h"
 
 #import "base/files/file_util.h"
+#import "components/bookmarks/test/bookmark_test_helpers.h"
+#import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/download/download_directory_util.h"
 #import "ios/chrome/browser/download/external_app_util.h"
@@ -72,6 +74,9 @@
     test_cbs_builder.AddTestingFactory(
         PrerenderServiceFactory::GetInstance(),
         PrerenderServiceFactory::GetDefaultFactory());
+    test_cbs_builder.AddTestingFactory(
+        ios::BookmarkModelFactory::GetInstance(),
+        ios::BookmarkModelFactory::GetDefaultFactory());
 
     chrome_browser_state_ = test_cbs_builder.Build();
     browser_ = std::make_unique<TestBrowser>(chrome_browser_state_.get());
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index 825195a..f3b3a6c 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -22,6 +22,7 @@
 
 @protocol ActivityServicePositioner;
 class Browser;
+@class BookmarkInteractionController;
 @class BrowserContainerViewController;
 @class BrowserViewControllerHelper;
 @class BubblePresenter;
@@ -59,6 +60,7 @@
   TabStripCoordinator* tabStripCoordinator;
   TabStripLegacyCoordinator* legacyTabStripCoordinator;
   SideSwipeController* sideSwipeController;
+  BookmarkInteractionController* bookmarkInteractionController;
 } BrowserViewControllerDependencies;
 
 // The top-level view controller for the browser UI. Manages other controllers
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index ce19745..29e450d 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -516,6 +516,7 @@
     _sideSwipeController = dependencies.sideSwipeController;
     [_sideSwipeController setSnapshotDelegate:self];
     [_sideSwipeController setSwipeDelegate:self];
+    _bookmarkInteractionController = dependencies.bookmarkInteractionController;
     self.toolbarInterface = dependencies.toolbarInterface;
     self.primaryToolbarCoordinator = dependencies.primaryToolbarCoordinator;
     self.secondaryToolbarCoordinator = dependencies.secondaryToolbarCoordinator;
@@ -1096,7 +1097,6 @@
   _fullscreenDisabler = nullptr;
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 
-  [_bookmarkInteractionController shutdown];
   _bookmarkInteractionController = nil;
 }
 
@@ -2182,16 +2182,6 @@
   webState->WasShown();
 }
 
-// Initializes the bookmark interaction controller if not already initialized.
-- (void)initializeBookmarkInteractionController {
-  if (_bookmarkInteractionController)
-    return;
-  // TODO(crbug.com/1329103): Remove BookmarkInteractionController from BVC.
-  _bookmarkInteractionController =
-      [[BookmarkInteractionController alloc] initWithBrowser:self.browser
-                                            parentController:self];
-}
-
 - (void)updateOverlayContainerOrder {
   // Both infobar overlay container views should exist in front of the entire
   // browser UI, and the banner container should appear behind the modal
@@ -3662,8 +3652,6 @@
 #pragma mark - BrowserCommands
 
 - (void)bookmarkCurrentPage {
-  [self initializeBookmarkInteractionController];
-
   GURL URL = self.currentWebState->GetLastCommittedURL();
   // TODO(crbug.com/1329102): Change -isWebStateBookmarkedByUser method to a
   // free function.
@@ -3693,12 +3681,6 @@
   [_voiceSearchController prepareToAppear];
 }
 
-// TODO(crbug.com/1329107): Move `showBookmarksManager` out of the BVC.
-- (void)showBookmarksManager {
-  [self initializeBookmarkInteractionController];
-  [_bookmarkInteractionController presentBookmarks];
-}
-
 // TODO(crbug.com/1272498): Refactor this command away, and add a mediator to
 // observe the active web state closing and push updates into the BVC for UI
 // work.
@@ -4284,10 +4266,6 @@
   return _toolbarAccessoryPresenter;
 }
 
-#pragma mark - ManageAccountsDelegate
-// TODO(crbug.com/1272476): Factor ManageAccountsDelegate out of the BVC. It can
-// be a browser agent instead.
-
 #pragma mark - SigninPresenter
 
 - (void)showSignin:(ShowSigninCommand*)command {
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
index 6ece410c..9c80ed2 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_unittest.mm
@@ -12,6 +12,7 @@
 
 #import "components/open_from_clipboard/fake_clipboard_recent_content.h"
 #import "components/search_engines/template_url_service.h"
+#import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/favicon/favicon_service_factory.h"
 #import "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
@@ -24,6 +25,7 @@
 #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
 #import "ios/chrome/browser/sessions/test_session_service.h"
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_interaction_controller.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_view_controller.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller_helper.h"
 #import "ios/chrome/browser/ui/browser_view/key_commands_provider.h"
@@ -103,6 +105,9 @@
     test_cbs_builder.AddTestingFactory(
         ios::HistoryServiceFactory::GetInstance(),
         ios::HistoryServiceFactory::GetDefaultFactory());
+    test_cbs_builder.AddTestingFactory(
+        ios::BookmarkModelFactory::GetInstance(),
+        ios::BookmarkModelFactory::GetDefaultFactory());
 
     chrome_browser_state_ = test_cbs_builder.Build();
 
@@ -210,6 +215,9 @@
     side_swipe_controller_ =
         [[SideSwipeController alloc] initWithBrowser:browser_.get()];
 
+    bookmark_interaction_controller_ =
+        [[BookmarkInteractionController alloc] initWithBrowser:browser_.get()];
+
     BrowserViewControllerDependencies dependencies;
     dependencies.prerenderService = fake_prerender_service_.get();
     dependencies.bubblePresenter = bubble_presenter_;
@@ -220,6 +228,8 @@
     dependencies.tabStripCoordinator = tab_strip_coordinator_;
     dependencies.legacyTabStripCoordinator = legacy_tab_strip_coordinator_;
     dependencies.sideSwipeController = side_swipe_controller_;
+    dependencies.bookmarkInteractionController =
+        bookmark_interaction_controller_;
 
     bvc_ = [[BrowserViewController alloc]
                        initWithBrowser:browser_.get()
@@ -272,6 +282,7 @@
   TabStripCoordinator* tab_strip_coordinator_;
   TabStripLegacyCoordinator* legacy_tab_strip_coordinator_;
   SideSwipeController* side_swipe_controller_;
+  BookmarkInteractionController* bookmark_interaction_controller_;
 };
 
 TEST_F(BrowserViewControllerTest, TestWebStateSelected) {
diff --git a/ios/chrome/browser/ui/commands/browser_commands.h b/ios/chrome/browser/ui/commands/browser_commands.h
index 611e5f7..c39a54e 100644
--- a/ios/chrome/browser/ui/commands/browser_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_commands.h
@@ -55,9 +55,6 @@
 // Closes all tabs.
 - (void)closeAllTabs;
 
-// Shows the bookmarks manager.
-- (void)showBookmarksManager;
-
 // Prepares the browser to display a popup menu.
 - (void)prepareForPopupMenuPresentation:(PopupMenuCommandType)type;
 
diff --git a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
index 0248b748..7328904c 100644
--- a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
@@ -43,6 +43,9 @@
 // applicable.
 - (void)showDefaultSiteViewIPH;
 
+// Shows bookmarks manager.
+- (void)showBookmarksManager;
+
 // Shows recent tabs.
 - (void)showRecentTabs;
 
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
index aba04c7..681d714 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
@@ -6,6 +6,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "components/bookmarks/test/bookmark_test_helpers.h"
+#import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/favicon/favicon_service_factory.h"
 #import "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
@@ -82,6 +84,9 @@
     test_cbs_builder.AddTestingFactory(
         PrerenderServiceFactory::GetInstance(),
         PrerenderServiceFactory::GetDefaultFactory());
+    test_cbs_builder.AddTestingFactory(
+        ios::BookmarkModelFactory::GetInstance(),
+        ios::BookmarkModelFactory::GetDefaultFactory());
 
     chrome_browser_state_ = test_cbs_builder.Build();
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
index 7e4c183..4d80c13 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -507,8 +507,8 @@
 - (BookmarkInteractionController*)bookmarkInteractionController {
   if (!_bookmarkInteractionController) {
     _bookmarkInteractionController = [[BookmarkInteractionController alloc]
-         initWithBrowser:self.regularBrowser
-        parentController:self.baseViewController];
+        initWithBrowser:self.regularBrowser];
+    _bookmarkInteractionController.parentController = self.baseViewController;
   }
   return _bookmarkInteractionController;
 }
diff --git a/ios/web_view/internal/sync/cwv_trusted_vault_utils.mm b/ios/web_view/internal/sync/cwv_trusted_vault_utils.mm
index 818b8ab..e352fea 100644
--- a/ios/web_view/internal/sync/cwv_trusted_vault_utils.mm
+++ b/ios/web_view/internal/sync/cwv_trusted_vault_utils.mm
@@ -44,7 +44,9 @@
 }
 
 + (void)logTrustedVaultDidReceiveHTTPStatusCode:(NSInteger)statusCode {
-  syncer::RecordTrustedVaultURLFetchResponse(statusCode, /*net_error=*/0);
+  syncer::RecordTrustedVaultURLFetchResponse(
+      statusCode, /*net_error=*/0,
+      syncer::TrustedVaultURLFetchReasonForUMA::kUnspecified);
 }
 
 + (void)logTrustedVaultDidFailKeyDistribution:(NSError*)error {
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 3f80b62c..e597bd8b 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -323,10 +323,12 @@
       "video/chromeos/video_capture_jpeg_decoder_impl.cc",
       "video/chromeos/video_capture_jpeg_decoder_impl.h",
     ]
-    public_deps += [ "//media/capture/video/chromeos/public" ]
+    public_deps += [
+      "//ash/webui/camera_app_ui:document_scanning",
+      "//media/capture/video/chromeos/public",
+    ]
     deps += [
       "//ash/constants",
-      "//ash/webui/camera_app_ui:document_scanning",
       "//build/config/linux/libdrm",
       "//chromeos/components/sensors:sensors",
       "//chromeos/dbus/power",
diff --git a/media/gpu/gpu_video_encode_accelerator_factory.cc b/media/gpu/gpu_video_encode_accelerator_factory.cc
index 604c7da..60f79c1 100644
--- a/media/gpu/gpu_video_encode_accelerator_factory.cc
+++ b/media/gpu/gpu_video_encode_accelerator_factory.cc
@@ -44,8 +44,14 @@
   scoped_refptr<V4L2Device> device = V4L2Device::Create();
   if (!device)
     return nullptr;
+#if BUILDFLAG(IS_CHROMEOS)
+  // TODO(crbug.com/901264): Encoders use hack for passing offset within
+  // a DMA-buf, which is not supported upstream.
   return base::WrapUnique<VideoEncodeAccelerator>(
       new V4L2VideoEncodeAccelerator(std::move(device)));
+#else
+  return nullptr;
+#endif
 }
 #endif
 
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index a4a0d4d..68d64f98 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -60,20 +60,28 @@
     "v4l2_video_decoder_backend_stateless.h",
     "v4l2_video_decoder_delegate_h264.cc",
     "v4l2_video_decoder_delegate_h264.h",
-    "v4l2_video_decoder_delegate_h264_legacy.cc",
-    "v4l2_video_decoder_delegate_h264_legacy.h",
     "v4l2_video_decoder_delegate_vp8.cc",
     "v4l2_video_decoder_delegate_vp8.h",
-    "v4l2_video_decoder_delegate_vp8_legacy.cc",
-    "v4l2_video_decoder_delegate_vp8_legacy.h",
     "v4l2_video_decoder_delegate_vp9.cc",
     "v4l2_video_decoder_delegate_vp9.h",
-    "v4l2_video_decoder_delegate_vp9_legacy.cc",
-    "v4l2_video_decoder_delegate_vp9_legacy.h",
-    "v4l2_video_encode_accelerator.cc",
-    "v4l2_video_encode_accelerator.h",
   ]
 
+  if (is_chromeos) {
+    sources += [
+      "v4l2_video_decoder_delegate_h264_legacy.cc",
+      "v4l2_video_decoder_delegate_h264_legacy.h",
+      "v4l2_video_decoder_delegate_vp8_legacy.cc",
+      "v4l2_video_decoder_delegate_vp8_legacy.h",
+      "v4l2_video_decoder_delegate_vp9_legacy.cc",
+      "v4l2_video_decoder_delegate_vp9_legacy.h",
+
+      # TODO(crbug.com/901264): Encoders use hack for passing offset
+      # within a DMA-buf, which is not supported upstream.
+      "v4l2_video_encode_accelerator.cc",
+      "v4l2_video_encode_accelerator.h",
+    ]
+  }
+
   libs = [
     "EGL",
     "GLESv2",
diff --git a/media/gpu/v4l2/v4l2_decode_surface.cc b/media/gpu/v4l2/v4l2_decode_surface.cc
index 456ad95..e7bc695 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.cc
+++ b/media/gpu/v4l2/v4l2_decode_surface.cc
@@ -103,6 +103,8 @@
   return out;
 }
 
+// ConfigStore is ChromeOS-specific legacy stuff
+#if BUILDFLAG(IS_CHROMEOS)
 void V4L2ConfigStoreDecodeSurface::PrepareSetCtrls(
     struct v4l2_ext_controls* ctrls) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -140,6 +142,7 @@
 
   return false;
 }
+#endif
 
 void V4L2RequestDecodeSurface::PrepareSetCtrls(
     struct v4l2_ext_controls* ctrls) const {
diff --git a/media/gpu/v4l2/v4l2_decode_surface.h b/media/gpu/v4l2/v4l2_decode_surface.h
index fefe201..2f188a1 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.h
+++ b/media/gpu/v4l2/v4l2_decode_surface.h
@@ -111,6 +111,9 @@
   std::vector<scoped_refptr<V4L2DecodeSurface>> reference_surfaces_;
 };
 
+// ConfigStore is ChromeOS-specific legacy stuff
+// TODO(b/222774780): Remove when all legacy implementations are gone.
+#if BUILDFLAG(IS_CHROMEOS)
 // An implementation of V4L2DecodeSurface that uses the config store to
 // associate controls/buffers to frames.
 class V4L2ConfigStoreDecodeSurface : public V4L2DecodeSurface {
@@ -138,6 +141,7 @@
   // The configuration store of the input buffer.
   uint32_t config_store_;
 };
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 // An implementation of V4L2DecodeSurface that uses requests to associate
 // controls/buffers to frames
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 241b328..5ae926d 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -849,12 +849,15 @@
   return buffer_data_->v4l2_buffer_.index;
 }
 
+// ConfigStore is ChromeOS-specific legacy stuff
+#if BUILDFLAG(IS_CHROMEOS)
 void V4L2WritableBufferRef::SetConfigStore(uint32_t config_store) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(buffer_data_);
 
   buffer_data_->v4l2_buffer_.config_store = config_store;
 }
+#endif
 
 V4L2ReadableBuffer::V4L2ReadableBuffer(const struct v4l2_buffer& v4l2_buffer,
                                        base::WeakPtr<V4L2Queue> queue,
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 70f47ae..ba0600b5 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -179,10 +179,13 @@
   // removed. See crbug/879971
   size_t BufferId() const;
 
+  // ConfigStore is ChromeOS-specific legacy stuff
+#if BUILDFLAG(IS_CHROMEOS)
   // Set the passed config store to this buffer.
   // This method is only used for backward compatibility until the config
   // store is deprecated and should not be called by new code.
   void SetConfigStore(uint32_t config_store);
+#endif
 
   V4L2WritableBufferRef(const V4L2WritableBufferRef&) = delete;
   V4L2WritableBufferRef& operator=(const V4L2WritableBufferRef&) = delete;
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index ffecb96..9e40a53 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -294,10 +294,15 @@
           std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
           video_profile_, config.container_color_space);
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       decoder_ = std::make_unique<H264Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateH264Legacy>(this,
                                                                device_.get()),
           video_profile_, config.container_color_space);
+#else
+      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
+      return false;
+#endif
     }
   } else if (video_profile_ >= VP8PROFILE_MIN &&
              video_profile_ <= VP8PROFILE_MAX) {
@@ -306,10 +311,15 @@
           std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
           config.container_color_space);
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       decoder_ = std::make_unique<VP8Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateVP8Legacy>(this,
                                                               device_.get()),
           config.container_color_space);
+#else
+      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
+      return false;
+#endif
     }
   } else if (video_profile_ >= VP9PROFILE_MIN &&
              video_profile_ <= VP9PROFILE_MAX) {
@@ -327,10 +337,15 @@
           video_profile_, config.container_color_space);
 
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       decoder_ = std::make_unique<VP9Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateVP9Legacy>(this,
                                                               device_.get()),
           video_profile_, config.container_color_space);
+#else
+      NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
+      return false;
+#endif
     }
   } else {
     NOTREACHED() << "Unsupported profile " << GetProfileName(video_profile_);
@@ -2135,9 +2150,15 @@
         std::move(*input_buffer), std::move(*output_buffer),
         output_record.output_frame, std::move(*request_ref));
   } else {
+// ConfigStore is ChromeOS-specific legacy stuff
+#if BUILDFLAG(IS_CHROMEOS)
     dec_surface = new V4L2ConfigStoreDecodeSurface(std::move(*input_buffer),
                                                    std::move(*output_buffer),
                                                    output_record.output_frame);
+#else
+    NOTREACHED() << "ConfigStore not supported.";
+    return nullptr;
+#endif
   }
 
   DVLOGF(4) << "Created surface " << input << " -> " << output;
diff --git a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
index 0f2181d0..1135c6e 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
@@ -282,8 +282,15 @@
         std::move(*input_buf), std::move(*output_buf), std::move(frame),
         std::move(*request_ref));
   } else {
+    // ConfigStore is ChromeOS-specific legacy stuff
+    // TODO(b/222774780): Remove when all legacy implementations are gone.
+#if BUILDFLAG(IS_CHROMEOS)
     dec_surface = new V4L2ConfigStoreDecodeSurface(
         std::move(*input_buf), std::move(*output_buf), std::move(frame));
+#else
+    NOTREACHED() << "ConfigStore not supported.";
+    return nullptr;
+#endif
   }
 
   return dec_surface;
@@ -692,10 +699,15 @@
           std::make_unique<V4L2VideoDecoderDelegateH264>(this, device_.get()),
           profile_, color_space_);
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       avd_ = std::make_unique<H264Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateH264Legacy>(this,
                                                                device_.get()),
           profile_, color_space_);
+#else
+      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
+      return false;
+#endif
     }
   } else if (profile_ >= VP8PROFILE_MIN && profile_ <= VP8PROFILE_MAX) {
     if (input_queue_->SupportsRequests()) {
@@ -703,10 +715,15 @@
           std::make_unique<V4L2VideoDecoderDelegateVP8>(this, device_.get()),
           color_space_);
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       avd_ = std::make_unique<VP8Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateVP8Legacy>(this,
                                                               device_.get()),
           color_space_);
+#else
+      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
+      return false;
+#endif
     }
   } else if (profile_ >= VP9PROFILE_MIN && profile_ <= VP9PROFILE_MAX) {
     if (input_queue_->SupportsRequests()) {
@@ -722,10 +739,15 @@
           std::make_unique<V4L2VideoDecoderDelegateVP9>(this, device_.get()),
           profile_, color_space_);
     } else {
+#if BUILDFLAG(IS_CHROMEOS)
       avd_ = std::make_unique<VP9Decoder>(
           std::make_unique<V4L2VideoDecoderDelegateVP9Legacy>(this,
                                                               device_.get()),
           profile_, color_space_);
+#else
+      VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
+      return false;
+#endif
     }
   } else {
     VLOGF(1) << "Unsupported profile " << GetProfileName(profile_);
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc
index 6b6c8c83..9f37ddcc 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc
@@ -4,11 +4,15 @@
 
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_h264.h"
 
+// ChromeOS specific header; does not exist upstream
+#if BUILDFLAG(IS_CHROMEOS)
 // TODO(987856): prevent legacy headers being included from videodev2.h until
 // v4.14 support is deprecated.
 #define _H264_CTRLS_LEGACY_H_
 
 #include <linux/media/h264-ctrls-upstream.h>
+#endif
+
 #include <linux/videodev2.h>
 #include <type_traits>
 
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.cc
index 085440f..3523c83 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp8.cc
@@ -4,8 +4,12 @@
 
 #include "v4l2_video_decoder_delegate_vp8.h"
 
+// ChromeOS specific header; does not exist upstream
+#if BUILDFLAG(IS_CHROMEOS)
 #define __LINUX_MEDIA_VP8_CTRLS_LEGACY_H
 #include <linux/media/vp8-ctrls-upstream.h>
+#endif
+
 #include <linux/videodev2.h>
 
 #include <type_traits>
diff --git a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.cc b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.cc
index 3a31e939..be3008d 100644
--- a/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.cc
@@ -4,7 +4,10 @@
 
 #include "media/gpu/v4l2/v4l2_video_decoder_delegate_vp9.h"
 
+// ChromeOS specific header; does not exist upstream
+#if BUILDFLAG(IS_CHROMEOS)
 #include <linux/media/vp9-ctrls-upstream.h>
+#endif
 
 #include "base/logging.h"
 #include "base/numerics/safe_math.h"
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index a70362d..7f54392 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -45,6 +45,7 @@
 #define NOTIFY_RETURN_ON_FAILURE(cond, log, ret) \
   do {                                           \
     if (cond) {                                  \
+      SetState(kError);                          \
       MEDIA_LOG(ERROR, media_log.get()) << log;  \
       NotifyError(kPlatformFailureError);        \
       return ret;                                \
@@ -255,6 +256,15 @@
   std::vector<uint8_t> data_;
 };
 
+struct MediaFoundationVideoEncodeAccelerator::PendingInput {
+  PendingInput(scoped_refptr<media::VideoFrame> frame, bool force_keyframe)
+      : frame(std::move(frame)), force_keyframe(force_keyframe) {}
+  ~PendingInput() {}
+
+  scoped_refptr<media::VideoFrame> frame;
+  bool force_keyframe;
+};
+
 struct MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef {
   BitstreamBufferRef() = delete;
 
@@ -281,6 +291,7 @@
           gpu_preferences.enable_media_foundation_vea_on_windows7),
       disable_dynamic_framerate_update_(
           gpu_workarounds.disable_dynamic_video_encode_framerate_update),
+      state_(kUninitialized),
       frame_rate_(kMaxFrameRateNumerator / kMaxFrameRateDenominator),
       bitrate_(Bitrate::ConstantBitrate(kDefaultTargetBitrate)),
       input_required_(false),
@@ -296,7 +307,7 @@
     ~MediaFoundationVideoEncodeAccelerator() {
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
+  DCHECK(async_callback_ref_.IsOne());
   DCHECK(!encoder_task_weak_factory_.HasWeakPtrs());
 }
 
@@ -532,6 +543,11 @@
         hr, "Couldn't set ProcessMessage MFT_MESSAGE_SET_D3D_MANAGER", );
   }
 
+  hr = encoder_->QueryInterface(IID_PPV_ARGS(&event_generator_));
+  NOTIFY_RETURN_ON_HR_FAILURE(hr, "Couldn't get event generator", );
+
+  event_generator_->BeginGetEvent(this, nullptr);
+
   // Start the asynchronous processing model
   hr = encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
   NOTIFY_RETURN_ON_HR_FAILURE(
@@ -542,13 +558,6 @@
   hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
   NOTIFY_RETURN_ON_HR_FAILURE(
       hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_START_OF_STREAM", );
-  hr = encoder_->QueryInterface(IID_PPV_ARGS(&event_generator_));
-  NOTIFY_RETURN_ON_HR_FAILURE(hr, "Couldn't get event generator", );
-
-  main_client_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, main_client_,
-                                kNumInputBuffers, input_visible_size_,
-                                bitstream_buffer_size_));
 
   VideoEncoderInfo encoder_info;
   encoder_info.implementation_name = "MediaFoundationVideoEncodeAccelerator";
@@ -935,57 +944,30 @@
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
 
-  bool input_delivered = false;
   HRESULT hr = E_FAIL;
-  if (input_required_) {
-    // Hardware MFT is waiting for this coming input.
-    hr = ProcessInput(std::move(frame), force_keyframe);
-    if (FAILED(hr)) {
-      NotifyError(kPlatformFailureError);
-      RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
-    }
-
-    DVLOG(3) << "Sent for encode " << hr;
-    input_delivered = true;
-    input_required_ = false;
-  } else {
-    Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
-    hr = event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
-    if (FAILED(hr)) {
-      DLOG(WARNING) << "Abandoned input frame for video encoder.";
-      return;
-    }
-
-    MediaEventType event_type;
-    hr = media_event->GetType(&event_type);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get the type of media event.";
-      return;
-    }
-
-    // Always deliver the current input into HMFT.
-    if (event_type == METransformNeedInput) {
-      hr = ProcessInput(std::move(frame), force_keyframe);
-      if (FAILED(hr)) {
-        NotifyError(kPlatformFailureError);
-        RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
-      }
-
-      DVLOG(3) << "Sent for encode " << hr;
-      input_delivered = true;
-    } else if (event_type == METransformHaveOutput) {
-      ProcessOutput();
-      input_delivered =
-          TryToDeliverInputFrame(std::move(frame), force_keyframe);
-    }
-  }
-
-  if (!input_delivered) {
-    DLOG(ERROR) << "Failed to deliver input frame to video encoder";
+  if (state_ != kEncoding) {
+    DLOG(WARNING) << "Abandon input frame for video encoder.";
     return;
   }
 
-  TryToReturnBitstreamBuffer();
+  pending_input_queue_.push_back(
+      PendingInput(std::move(frame), force_keyframe));
+
+  // Before the first time |input_required_| is toggled on, the
+  // HMFT is not yet ready receiving inputs.
+  if (input_required_) {
+    PendingInput pending_input = pending_input_queue_.front();
+    pending_input_queue_.pop_front();
+    hr = ProcessInput(std::move(pending_input.frame),
+                      pending_input.force_keyframe);
+    if (FAILED(hr)) {
+      DLOG(ERROR) << "Failed to encode pending frame.";
+      SetState(kError);
+      NotifyError(kPlatformFailureError);
+      return;
+    }
+    input_required_ = false;
+  }
 }
 
 HRESULT MediaFoundationVideoEncodeAccelerator::ProcessInput(
@@ -1338,89 +1320,80 @@
   BitstreamBufferMetadata md(size, keyframe, timestamp);
   if (codec_ == VideoCodec::kH264 && temporalScalableCoding())
     md.h264.emplace().temporal_idx = temporal_id;
+
   main_client_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&Client::BitstreamBufferReady, main_client_,
                                 buffer_ref->id, md));
 }
 
-bool MediaFoundationVideoEncodeAccelerator::TryToDeliverInputFrame(
-    scoped_refptr<VideoFrame> frame,
-    bool force_keyframe) {
-  bool input_delivered = false;
-  Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
-  MediaEventType event_type;
-  do {
-    HRESULT hr =
-        event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
-    if (FAILED(hr)) {
-      break;
-    }
+void MediaFoundationVideoEncodeAccelerator::OnInitCompleted() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
 
-    hr = media_event->GetType(&event_type);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get the type of media event.";
-      break;
-    }
-
-    switch (event_type) {
-      case METransformHaveOutput: {
-        ProcessOutput();
-        continue;
-      }
-      case METransformNeedInput: {
-        hr = ProcessInput(std::move(frame), force_keyframe);
-        if (FAILED(hr)) {
-          NotifyError(kPlatformFailureError);
-          RETURN_ON_HR_FAILURE(hr, "Couldn't encode", false);
-        }
-
-        DVLOG(3) << "Sent for encode " << hr;
-        return true;
-      }
-      default:
-        break;
-    }
-  } while (true);
-
-  return input_delivered;
+  DVLOG(3) << "Encoder is ready to accept input.";
+  SetState(kEncoding);
 }
 
-void MediaFoundationVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
-  // Try to fetch the encoded frame in time.
-  bool output_processed = false;
-  do {
-    Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
-    MediaEventType event_type;
-    HRESULT hr =
-        event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
-    if (FAILED(hr)) {
-      if (!output_processed) {
-        continue;
-      } else {
-        break;
-      }
-    }
+void MediaFoundationVideoEncodeAccelerator::MediaEventHandler(
+    MediaEventType event_type) {
+  DVLOG(3) << __func__;
+  DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
+  DCHECK(event_generator_);
 
-    hr = media_event->GetType(&event_type);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get the type of media event.";
+  // |input_required_| needs to be reset to false on events except
+  // METransformNeedInput. Otherwise next ProcessInput() will fail
+  // with MF_E_NOTACCEPTING.
+  input_required_ = false;
+  switch (event_type) {
+    case METransformNeedInput: {
+      if (state_ == kUninitialized) {
+        SetState(kInitializing);
+        // HMFT is not ready for receiving inputs until the first
+        // METransformNeedInput event is published.
+        main_client_task_runner_->PostTaskAndReply(
+            FROM_HERE,
+            base::BindOnce(&Client::RequireBitstreamBuffers, main_client_,
+                           kNumInputBuffers, input_visible_size_,
+                           bitstream_buffer_size_),
+            base::BindOnce(
+                &MediaFoundationVideoEncodeAccelerator::OnInitCompleted,
+                encoder_weak_ptr_));
+        input_required_ = true;
+      } else if (state_ == kEncoding) {
+        // If there're already pending inputs in queue, that needs to
+        // be handled here immediately.
+        if (!pending_input_queue_.empty()) {
+          PendingInput pending_input = pending_input_queue_.front();
+          pending_input_queue_.pop_front();
+          HRESULT hr = ProcessInput(std::move(pending_input.frame),
+                                    pending_input.force_keyframe);
+          if (FAILED(hr)) {
+            DLOG(ERROR) << "Failed to encode pending frame.";
+            SetState(kError);
+            NotifyError(kPlatformFailureError);
+            return;
+          }
+        } else {
+          input_required_ = true;
+        }
+      } else {
+        // We have recovered from a stream restart.
+        SetState(kEncoding);
+        input_required_ = true;
+      }
       break;
     }
-
-    switch (event_type) {
-      case METransformHaveOutput: {
-        ProcessOutput();
-        output_processed = true;
-        break;
-      }
-      case METransformNeedInput: {
-        input_required_ = true;
-        continue;
-      }
-      default:
-        break;
+    case METransformHaveOutput: {
+      ProcessOutput();
+      break;
     }
-  } while (true);
+    case METransformDrainComplete: {
+      RestartStream();
+      break;
+    }
+    default:
+      break;
+  }
+  event_generator_->BeginGetEvent(this, nullptr);
 }
 
 void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
@@ -1479,46 +1452,13 @@
                              framerate, 1);
     RETURN_ON_HR_FAILURE(hr, "Couldn't set frame rate for input type", );
 
-    // Some HMFTs will reject output type change with MF_E_INVALIDTYPE due
-    // to temporary mismatch between output/input media types, so we always
-    // clear the input/output media types before reconfiguring them
-    // dynamically.
     hr = encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
     RETURN_ON_HR_FAILURE(
         hr, "Couldn't process message MFT_MESSAGE_COMMAND_DRAIN", );
 
-    DrainPendingOutputs();
-
-    hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
-    RETURN_ON_HR_FAILURE(
-        hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_OF_STREAM", );
-
-    hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
-    RETURN_ON_HR_FAILURE(
-        hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_STREAMING", );
-
-    hr = encoder_->SetInputType(input_stream_id_, nullptr, 0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't clear input media type.", );
-
-    hr = encoder_->SetOutputType(output_stream_id_, nullptr, 0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't clear ouput media type.", );
-
-    hr = encoder_->SetOutputType(output_stream_id_,
-                                 imf_output_media_type_.Get(), 0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", );
-
-    hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(),
-                                0);
-    RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", );
-
-    hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
-    RETURN_ON_HR_FAILURE(
-        hr, "Couldn't process message MFT_MESSAGE_NOTIFY_BEGIN_STREAMING", );
-
-    hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
-    RETURN_ON_HR_FAILURE(
-        hr, "Couldn't process message MFT_MESSAGE_NOTIFY_START_OF_STREAM", );
-
+    // Stop queueing frames until METransformNeedInput is received. Stream
+    // will be restarted after draining complete.
+    SetState(kDraining);
     frame_rate_ = framerate;
   }
 
@@ -1542,6 +1482,51 @@
   }
 }
 
+void MediaFoundationVideoEncodeAccelerator::RestartStream() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
+  HRESULT hr = S_OK;
+
+  hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
+  RETURN_ON_HR_FAILURE(
+      hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_OF_STREAM", );
+
+  hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
+  RETURN_ON_HR_FAILURE(
+      hr, "Couldn't process message MFT_MESSAGE_NOTIFY_END_STREAMING", );
+
+  // Some HMFTs will reject output type change with MF_E_INVALIDTYPE due
+  // to temporary mismatch between output/input media types, so we always
+  // clear the input/output media types before reconfiguring them
+  // dynamically.
+  hr = encoder_->SetInputType(input_stream_id_, nullptr, 0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't clear input media type.", );
+
+  hr = encoder_->SetOutputType(output_stream_id_, nullptr, 0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't clear ouput media type.", );
+
+  hr = encoder_->SetOutputType(output_stream_id_, imf_output_media_type_.Get(),
+                               0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", );
+
+  hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(), 0);
+  RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", );
+
+  hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+  RETURN_ON_HR_FAILURE(
+      hr, "Couldn't process message MFT_MESSAGE_NOTIFY_BEGIN_STREAMING", );
+
+  hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+  RETURN_ON_HR_FAILURE(
+      hr, "Couldn't process message MFT_MESSAGE_NOTIFY_START_OF_STREAM", );
+}
+
+void MediaFoundationVideoEncodeAccelerator::SetState(State state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
+
+  DVLOG(3) << "Setting state to: " << state;
+  state_ = state;
+}
+
 void MediaFoundationVideoEncodeAccelerator::DestroyTask() {
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(encode_sequence_checker_);
@@ -1553,10 +1538,9 @@
 }
 
 void MediaFoundationVideoEncodeAccelerator::ReleaseEncoderResources() {
-  while (!bitstream_buffer_queue_.empty())
-    bitstream_buffer_queue_.pop_front();
-  while (!encoder_output_queue_.empty())
-    encoder_output_queue_.pop_front();
+  bitstream_buffer_queue_.clear();
+  pending_input_queue_.clear();
+  encoder_output_queue_.clear();
 
   if (activate_.Get() != nullptr) {
     activate_->ShutdownObject();
@@ -1733,22 +1717,58 @@
   return hr;
 }
 
-void MediaFoundationVideoEncodeAccelerator::DrainPendingOutputs() {
+HRESULT MediaFoundationVideoEncodeAccelerator::GetParameters(DWORD* pdwFlags,
+                                                             DWORD* pdwQueue) {
+  // Implementation of this method is optional.
+  return E_NOTIMPL;
+}
+
+HRESULT MediaFoundationVideoEncodeAccelerator::Invoke(
+    IMFAsyncResult* pAsyncResult) {
+  DCHECK(event_generator_);
+
+  HRESULT hr = S_OK;
   Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
+  MediaEventType event_type = MEUnknown;
 
-  while ((SUCCEEDED(
-      event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event)))) {
-    MediaEventType event_type;
-    HRESULT hr = media_event->GetType(&event_type);
-    if (FAILED(hr)) {
-      DLOG(ERROR) << "Failed to get the type of media event.";
-      continue;
-    }
+  hr = event_generator_->EndGetEvent(pAsyncResult, &media_event);
+  if (SUCCEEDED(hr)) {
+    // Get event type.
+    hr = media_event->GetType(&event_type);
 
-    if (event_type == METransformHaveOutput) {
-      ProcessOutput();
+    if (SUCCEEDED(hr)) {
+      hr = media_event->GetStatus(&hr);
+      if (SUCCEEDED(hr)) {
+        if (encoder_thread_task_runner_->BelongsToCurrentThread()) {
+          MediaEventHandler(event_type);
+        } else {
+          encoder_thread_task_runner_->PostTask(
+              FROM_HERE,
+              base::BindOnce(
+                  &MediaFoundationVideoEncodeAccelerator::MediaEventHandler,
+                  encoder_weak_ptr_, event_type));
+        }
+      }
     }
   }
+
+  return hr;
+}
+
+ULONG MediaFoundationVideoEncodeAccelerator::AddRef() {
+  return async_callback_ref_.Increment();
+}
+
+ULONG MediaFoundationVideoEncodeAccelerator::Release() {
+  DCHECK(!async_callback_ref_.IsOne());
+  return async_callback_ref_.Decrement() ? 1 : 0;
+}
+
+HRESULT MediaFoundationVideoEncodeAccelerator::QueryInterface(REFIID riid,
+                                                              void** ppv) {
+  static const QITAB qit[] = {
+      QITABENT(MediaFoundationVideoEncodeAccelerator, IMFAsyncCallback), {0}};
+  return QISearch(this, qit, riid, ppv);
 }
 
 }  // namespace media
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
index 5895a8a..c57fae0 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -13,11 +13,13 @@
 
 #include <memory>
 
+#include "base/atomic_ref_count.h"
 #include "base/bind.h"
 #include "base/containers/circular_deque.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread.h"
+#include "base/win/shlwapi.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "media/base/bitrate.h"
@@ -37,7 +39,8 @@
 // correct task runners. It starts an internal encoder thread on which
 // VideoEncodeAccelerator implementation tasks are posted.
 class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
-    : public VideoEncodeAccelerator {
+    : public VideoEncodeAccelerator,
+      public IMFAsyncCallback {
  public:
   explicit MediaFoundationVideoEncodeAccelerator(
       const gpu::GpuPreferences& gpu_preferences,
@@ -62,6 +65,13 @@
   void Destroy() override;
   bool IsGpuFrameResizeSupported() override;
 
+  // IMFAsyncCallback implementation
+  IFACEMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) override;
+  IFACEMETHODIMP Invoke(IMFAsyncResult* pAsyncResult) override;
+  IFACEMETHODIMP_(ULONG) AddRef() override;
+  IFACEMETHODIMP_(ULONG) Release() override;
+  IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+
   // Preloads dlls required for encoding. Returns true if all required dlls are
   // correctly loaded.
   static bool PreSandboxInitialization();
@@ -78,6 +88,19 @@
   // Holds output buffers coming from the encoder.
   class EncodeOutput;
 
+  // Pending encode input.
+  struct PendingInput;
+
+  // Encoder state.
+  enum State {
+    kUninitialized,
+    kInitializing,
+    kEncoding,
+    kDraining,
+    kFlushing,
+    kError,
+  };
+
   // Get supported profiles for specific codec.
   VideoEncodeAccelerator::SupportedProfiles GetSupportedProfilesForCodec(
       VideoCodec codec,
@@ -100,6 +123,9 @@
   // |main_client_task_runner_|.
   void NotifyError(VideoEncodeAccelerator::Error error);
 
+  // Set the encoder state to |state| on |encoder_thread_task_runner_|.
+  void SetState(State state);
+
   // Encoding task to be run on |encoder_thread_task_runner_|.
   void EncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
 
@@ -124,15 +150,11 @@
   // Checks for and copies encoded output on |encoder_thread_task_runner_|.
   void ProcessOutput();
 
-  // Drains pending output samples on |encoder_thread_task_runner_|.
-  void DrainPendingOutputs();
+  // After draining outputs, restart stream on |encoder_thread_task_runner_|.
+  void RestartStream();
 
-  // Tries to deliver the input frame to the encoder.
-  bool TryToDeliverInputFrame(scoped_refptr<VideoFrame> frame,
-                              bool force_keyframe);
-
-  // Tries to return a bitstream buffer to the client.
-  void TryToReturnBitstreamBuffer();
+  // Asynchronous event handler
+  void MediaEventHandler(MediaEventType event_type);
 
   // Inserts the output buffers for reuse on |encoder_thread_task_runner_|.
   void UseOutputBitstreamBufferTask(
@@ -158,6 +180,9 @@
   // Perform D3D11 scaling operation
   HRESULT PerformD3DScaling(ID3D11Texture2D* input_texture);
 
+  // Callback when the encoder is ready for accepting input.
+  void OnInitCompleted();
+
   const bool compatible_with_win7_;
   const bool disable_dynamic_framerate_update_;
 
@@ -165,6 +190,9 @@
   base::circular_deque<std::unique_ptr<BitstreamBufferRef>>
       bitstream_buffer_queue_;
 
+  // Input frame queue for encoding on next METransformNeedInput event.
+  base::circular_deque<PendingInput> pending_input_queue_;
+
   // EncodeOutput needs to be copied into a BitstreamBufferRef as a FIFO.
   base::circular_deque<std::unique_ptr<EncodeOutput>> encoder_output_queue_;
 
@@ -172,6 +200,9 @@
   // according to the corresponding layer pattern. Reset for every key frame.
   uint32_t outputs_since_keyframe_count_ = 0;
 
+  // Encoder state. Encode tasks will only run in kEncoding state.
+  State state_;
+
   // This parser is used to assign temporalId.
   H264Parser h264_parser_;
 
@@ -196,6 +227,7 @@
   Microsoft::WRL::ComPtr<IMFTransform> encoder_;
   Microsoft::WRL::ComPtr<ICodecAPI> codec_api_;
   Microsoft::WRL::ComPtr<IMFMediaEventGenerator> event_generator_;
+  base::AtomicRefCount async_callback_ref_{1};
 
   DWORD input_stream_id_;
   DWORD output_stream_id_;
@@ -204,6 +236,7 @@
   Microsoft::WRL::ComPtr<IMFMediaType> imf_output_media_type_;
 
   bool input_required_;
+
   Microsoft::WRL::ComPtr<IMFSample> input_sample_;
   Microsoft::WRL::ComPtr<IMFSample> output_sample_;
   Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor_;
diff --git a/remoting/host/chromeos/features.cc b/remoting/host/chromeos/features.cc
index 60b9ead..4adc58a39 100644
--- a/remoting/host/chromeos/features.cc
+++ b/remoting/host/chromeos/features.cc
@@ -8,5 +8,7 @@
 
 const base::Feature kEnableMultiMonitorsInCrd{"EnableMultiMonitorsInCrd",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kEnableCrdAdminRemoteAccess{
+    "EnableCrdAdminRemoteAccess", base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace remoting::features
diff --git a/remoting/host/chromeos/features.h b/remoting/host/chromeos/features.h
index e985524..067372b 100644
--- a/remoting/host/chromeos/features.h
+++ b/remoting/host/chromeos/features.h
@@ -11,6 +11,9 @@
 
 // Enable to allow CRD to stream other monitors than the primary display.
 extern const base::Feature kEnableMultiMonitorsInCrd;
+// Enable to allow CRD remote admin connections when the ChromeOS device is at
+// the login screen.
+extern const base::Feature kEnableCrdAdminRemoteAccess;
 
 }  // namespace remoting::features
 
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index ae89d105..d560a12 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -51,6 +51,7 @@
 
   deps = [
     "//build:chromeos_buildflags",
+    "//media/capture:capture_lib",
     "//media/capture:capture_switches",
   ]
 
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 8b0e81f..36c5389e 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8327,15 +8327,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8412,15 +8412,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8837,15 +8837,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8922,15 +8922,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index a690211..f25bb4f 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46457,15 +46457,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46542,15 +46542,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46967,15 +46967,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47052,15 +47052,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47481,15 +47481,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47566,15 +47566,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47991,15 +47991,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48076,15 +48076,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48573,15 +48573,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48658,15 +48658,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49083,15 +49083,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49168,15 +49168,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49665,15 +49665,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--client-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49750,15 +49750,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--client-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50175,15 +50175,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M103/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M103/out/Release",
           "--impl-version=103",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -50260,15 +50260,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--implementation-outdir",
-          "../../weblayer_instrumentation_test_M104/out/Release",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
+          "--webview-apk-path=apks/SystemWebView.apk",
+          "--implementation-outdir",
+          "../../weblayer_instrumentation_test_M104/out/Release",
           "--impl-version=104",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 38abb1f..b82bb03 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5688,21 +5688,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -5715,7 +5715,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
@@ -5853,21 +5853,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -5879,7 +5879,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "args": [
@@ -5999,21 +5999,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -6025,7 +6025,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a4807487..57f4ba8 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -92861,21 +92861,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -92883,7 +92883,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
@@ -92996,28 +92996,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "args": [
@@ -93117,28 +93117,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
@@ -94476,20 +94476,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -94503,7 +94503,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "merge": {
@@ -94641,20 +94641,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -94667,7 +94667,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "args": [
@@ -94787,20 +94787,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -94813,7 +94813,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "merge": {
@@ -96309,20 +96309,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -96336,7 +96336,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "merge": {
@@ -96474,20 +96474,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -96500,7 +96500,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "args": [
@@ -96620,20 +96620,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -96646,7 +96646,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "merge": {
@@ -97381,20 +97381,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -97407,7 +97407,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index ba050ab..3bda8226 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -19085,21 +19085,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5138.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -19112,7 +19112,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
@@ -19250,21 +19250,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -19276,7 +19276,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "args": [
@@ -19396,21 +19396,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5138.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5139.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5138.0",
-              "revision": "version:105.0.5138.0"
+              "location": "lacros_version_skew_tests_v105.0.5139.0",
+              "revision": "version:105.0.5139.0"
             }
           ],
           "dimension_sets": [
@@ -19422,7 +19422,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5138.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5139.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 7269a9e..61bc05d 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5138.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5139.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 105.0.5138.0',
+    'identifier': 'Lacros version skew testing ash 105.0.5139.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v105.0.5138.0',
-          'revision': 'version:105.0.5138.0',
+          'location': 'lacros_version_skew_tests_v105.0.5139.0',
+          'revision': 'version:105.0.5139.0',
         },
       ],
     },
@@ -529,16 +529,16 @@
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M104/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=104',
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
+      '--implementation-outdir',
+      '../../weblayer_instrumentation_test_M104/out/Release',
+      '--impl-version=104'
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -546,23 +546,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.24',
+          'revision': 'version:104.0.5112.24'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M103/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=103',
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
+      '--implementation-outdir',
+      '../../weblayer_instrumentation_test_M103/out/Release',
+      '--impl-version=103'
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -570,10 +570,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.64',
+          'revision': 'version:103.0.5060.64'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -673,16 +673,16 @@
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M104/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=104',
+      '--webview-apk-path=apks/SystemWebView.apk',
+      '--implementation-outdir',
+      '../../weblayer_instrumentation_test_M104/out/Release',
+      '--impl-version=104'
     ],
     'identifier': 'with_impl_from_104',
     'swarming': {
@@ -690,23 +690,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.24',
+          'revision': 'version:104.0.5112.24'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--implementation-outdir',
-      '../../weblayer_instrumentation_test_M103/out/Release',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--impl-version=103',
+      '--webview-apk-path=apks/SystemWebView.apk',
+      '--implementation-outdir',
+      '../../weblayer_instrumentation_test_M103/out/Release',
+      '--impl-version=103'
     ],
     'identifier': 'with_impl_from_103',
     'swarming': {
@@ -714,10 +714,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.64',
+          'revision': 'version:103.0.5060.64'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -817,16 +817,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M104/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--client-version=104',
+      '--webview-apk-path=apks/SystemWebView.apk',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M104/out/Release',
+      '--client-version=104'
     ],
     'identifier': 'with_client_from_104',
     'swarming': {
@@ -834,23 +834,23 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M104',
-          'revision': 'version:104.0.5112.24',
+          'revision': 'version:104.0.5112.24'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M103/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--client-version=103',
+      '--webview-apk-path=apks/SystemWebView.apk',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M103/out/Release',
+      '--client-version=103'
     ],
     'identifier': 'with_client_from_103',
     'swarming': {
@@ -858,10 +858,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M103',
-          'revision': 'version:103.0.5060.64',
+          'revision': 'version:103.0.5060.64'
         }
-      ],
-    },
+      ]
+    }
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 32f9fc9..d9ae5dd 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1876,23 +1876,6 @@
             ]
         }
     ],
-    "ButterForPasswordsRevisedOptInFlow": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20211203",
-                    "enable_features": [
-                        "PasswordsAccountStorageRevisedOptInFlow"
-                    ]
-                }
-            ]
-        }
-    ],
     "CCTNewDownloadTab": [
         {
             "platforms": [
@@ -8509,6 +8492,27 @@
             ]
         }
     ],
+    "UseHtmlAttributeNameLookup": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "UseHtmlAttributeNameLookup",
+                    "enable_features": [
+                        "UseHtmlAttributeNameLookup"
+                    ]
+                }
+            ]
+        }
+    ],
     "UseNativeThreadPool": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/build/scripts/make_element_attribute_name_lookup_trie.py b/third_party/blink/renderer/build/scripts/make_element_attribute_name_lookup_trie.py
new file mode 100755
index 0000000..c50739b
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/make_element_attribute_name_lookup_trie.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# 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.
+
+import sys
+
+from blinkbuild.name_style_converter import NameStyleConverter
+import json5_generator
+import trie_builder
+import template_expander
+
+
+class ElementAttributeNameLookupTrieWriter(json5_generator.Writer):
+    # TODO(https://crbug.com/1338945): Unify common keys.
+    # These are the names of the keys in the metadata section of the files.
+    # The writer requires an entry for each key in the 'metadata' section of
+    # the imported files.
+    default_metadata = {
+        'attrsNullNamespace': None,
+        'export': '',
+        'namespace': '',
+        'namespacePrefix': '',
+        'namespaceURI': '',
+    }
+    filters = {
+        'symbol':
+        lambda symbol: 'k' + NameStyleConverter(symbol).to_upper_camel_case()
+    }
+
+    def __init__(self, json5_file_paths, output_dir):
+        super(ElementAttributeNameLookupTrieWriter,
+              self).__init__(json5_file_paths, output_dir)
+        self._names = {}
+        for entry in self.json5_file.name_dictionaries:
+            self._names[entry['name'].original] = entry['name'].original
+        self._namespace = self.json5_file.metadata['namespace'].strip('"')
+        basename = self._namespace.lower(
+        ) + '_element_attribute_name_lookup_trie'
+        self._outputs = {
+            (basename + '.h'): self.generate_header,
+            (basename + '.cc'): self.generate_implementation,
+        }
+
+    @template_expander.use_jinja(
+        'templates/element_attribute_name_lookup_trie.h.tmpl')
+    def generate_header(self):
+        return {
+            'input_files': self._input_files,
+            'namespace': self._namespace,
+        }
+
+    @template_expander.use_jinja(
+        'templates/element_attribute_name_lookup_trie.cc.tmpl',
+        filters=filters)
+    def generate_implementation(self):
+        return {
+            'input_files': self._input_files,
+            'namespace': self._namespace,
+            'length_tries': trie_builder.trie_list_by_str_length(self._names)
+        }
+
+
+if __name__ == '__main__':
+    json5_generator.Maker(ElementAttributeNameLookupTrieWriter).main()
diff --git a/third_party/blink/renderer/build/scripts/make_element_lookup_trie.py b/third_party/blink/renderer/build/scripts/make_element_lookup_trie.py
index 8ae7e676e..57062f2 100755
--- a/third_party/blink/renderer/build/scripts/make_element_lookup_trie.py
+++ b/third_party/blink/renderer/build/scripts/make_element_lookup_trie.py
@@ -36,7 +36,7 @@
 
 
 class ElementLookupTrieWriter(json5_generator.Writer):
-    # FIXME: Inherit all these from somewhere.
+    # TODO(https://crbug.com/1338945): Inherit all these from somewhere.
     default_parameters = {
         'JSInterfaceName': {},
         'constructorNeedsCreateElementFlags': {},
diff --git a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl
new file mode 100644
index 0000000..4497025
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl
@@ -0,0 +1,24 @@
+{% from 'templates/macros.tmpl' import source_files_for_generated_file, trie_length_switch %}
+// 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.
+
+{{source_files_for_generated_file(template_file, input_files)}}
+
+#include "third_party/blink/renderer/core/{{namespace|lower}}_element_attribute_name_lookup_trie.h"
+
+#include "third_party/blink/renderer/core/{{namespace|lower}}_names.h"
+
+namespace blink {
+
+const QualifiedName& Lookup{{namespace}}AttributeName(const UChar* data, unsigned length) {
+  DCHECK(data);
+  DCHECK(length);
+  {% macro trie_return_statement(tag) -%}
+  {{namespace|lower}}_names::{{tag|symbol}}Attr
+  {%- endmacro %}
+  {{ trie_length_switch(length_tries, trie_return_statement, false) | indent(4) }}
+  return g_null_name;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl
new file mode 100644
index 0000000..cd0acd7
--- /dev/null
+++ b/third_party/blink/renderer/build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl
@@ -0,0 +1,23 @@
+{% from "templates/macros.tmpl" import license, source_files_for_generated_file %}
+{{ license() }}
+
+{{source_files_for_generated_file(template_file, input_files)}}
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_{{namespace|upper}}_ELEMENT_ATTRIBUTE_NAME_LOOKUP_TRIE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_{{namespace|upper}}_ELEMENT_ATTRIBUTE_NAME_LOOKUP_TRIE_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
+
+namespace blink {
+
+class QualifiedName;
+
+// Returns the QualifiedName for the attribute whose name matches `data`.
+// Returns `g_null_name` if there is no match. It is expected this is only
+// called if `length` is > 0.
+CORE_EXPORT const QualifiedName& Lookup{{namespace}}AttributeName(const UChar* data, unsigned length);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_{{namespace|upper}}_ELEMENT_ATTRIBUTE_NAME_LOOKUP_TRIE_H_
diff --git a/third_party/blink/renderer/controller/blink_initializer.cc b/third_party/blink/renderer/controller/blink_initializer.cc
index 42158513..b517c39 100644
--- a/third_party/blink/renderer/controller/blink_initializer.cc
+++ b/third_party/blink/renderer/controller/blink_initializer.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/execution_context/agent.h"
 #include "third_party/blink/renderer/core/frame/display_cutout_client_impl.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/parser/atomic_html_token.h"
 #include "third_party/blink/renderer/core/html/parser/literal_buffer.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
@@ -109,6 +110,11 @@
 const base::Feature kLiteralBufferCreateStringWithEncoding{
     "LiteralBufferCreateStringWithEncoding", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// See description of `g_use_html_attribute_name_lookup` in AtomicHTMLToken as
+// to what this controls.
+const base::Feature kUseHtmlAttributeNameLookup{
+    "UseHtmlAttributeNameLookup", base::FEATURE_DISABLED_BY_DEFAULT};
+
 Thread::TaskObserver* g_end_of_task_runner = nullptr;
 
 BlinkInitializer& GetBlinkInitializer() {
@@ -181,6 +187,9 @@
 
   g_literal_buffer_create_string_with_encoding =
       base::FeatureList::IsEnabled(kLiteralBufferCreateStringWithEncoding);
+
+  g_use_html_attribute_name_lookup =
+      base::FeatureList::IsEnabled(kUseHtmlAttributeNameLookup);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 5f3a8d7..3a74de72 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1079,6 +1079,32 @@
   deps = make_core_generated_deps
 }
 
+blink_python_runner("make_core_generated_html_attribute_name_lookup_trie") {
+  visibility = []  # Allow re-assignment of list.
+  visibility = [ ":*" ]
+  script = "../build/scripts/make_element_attribute_name_lookup_trie.py"
+
+  input_file = "html/html_attribute_names.json5"
+  inputs =
+      make_trie_helpers_files + [
+        input_file,
+        "../build/scripts/templates/element_attribute_name_lookup_trie.cc.tmpl",
+        "../build/scripts/templates/element_attribute_name_lookup_trie.h.tmpl",
+      ]
+  outputs = [
+    "$blink_core_output_dir/html_element_attribute_name_lookup_trie.cc",
+    "$blink_core_output_dir/html_element_attribute_name_lookup_trie.h",
+  ]
+
+  args = [
+    rebase_path(input_file, root_build_dir),
+    "--output_dir",
+    rel_blink_core_gen_dir,
+  ]
+
+  deps = make_core_generated_deps
+}
+
 blink_python_runner("make_core_generated_web_origin_trials") {
   script = "../build/scripts/make_web_origin_trials.py"
 
@@ -1141,6 +1167,11 @@
   ":make_core_generated_xml_ns_names",
 ]
 
+if (!is_android) {
+  targets_generating_sources +=
+      [ ":make_core_generated_html_attribute_name_lookup_trie" ]
+}
+
 group("all_generators") {
   public_deps = targets_generating_sources
   public_deps += [
diff --git a/third_party/blink/renderer/core/css/css_selector_list.cc b/third_party/blink/renderer/core/css/css_selector_list.cc
index 7748579ea..e6c1dfe 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.cc
+++ b/third_party/blink/renderer/core/css/css_selector_list.cc
@@ -107,11 +107,11 @@
   return specificity;
 }
 
-String CSSSelectorList::SelectorsText() const {
+String CSSSelectorList::SelectorsText(const CSSSelector* first) {
   StringBuilder result;
 
-  for (const CSSSelector* s = First(); s; s = Next(*s)) {
-    if (s != First())
+  for (const CSSSelector* s = first; s; s = Next(*s)) {
+    if (s != first)
       result.Append(", ");
     result.Append(s->SelectorText());
   }
diff --git a/third_party/blink/renderer/core/css/css_selector_list.h b/third_party/blink/renderer/core/css/css_selector_list.h
index d955fdd1..e9d9137 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.h
+++ b/third_party/blink/renderer/core/css/css_selector_list.h
@@ -109,7 +109,8 @@
     return SelectorIndex(*next);
   }
 
-  String SelectorsText() const;
+  String SelectorsText() const { return SelectorsText(First()); }
+  static String SelectorsText(const CSSSelector* first);
 
   // Selector lists don't know their length, computing it is O(n) and should be
   // avoided when possible. Instead iterate from first() and using next().
@@ -118,10 +119,10 @@
   // Return the specificity of the selector with the highest specificity.
   unsigned MaximumSpecificity() const;
 
- private:
   CSSSelectorList(const CSSSelectorList&) = delete;
   CSSSelectorList& operator=(const CSSSelectorList&) = delete;
 
+ private:
   // End of a multipart selector is indicated by is_last_in_tag_history_ bit in
   // the last item. End of the array is indicated by is_last_in_selector_list_
   // bit in the last item.
diff --git a/third_party/blink/renderer/core/css/css_style_rule.cc b/third_party/blink/renderer/core/css/css_style_rule.cc
index 27fb4bf..7d2790fe 100644
--- a/third_party/blink/renderer/core/css/css_style_rule.cc
+++ b/third_party/blink/renderer/core/css/css_style_rule.cc
@@ -65,7 +65,7 @@
   }
 
   DCHECK(!GetSelectorTextCache().Contains(this));
-  String text = style_rule_->SelectorList().SelectorsText();
+  String text = style_rule_->SelectorsText();
   GetSelectorTextCache().Set(this, text);
   SetHasCachedSelectorText(true);
   return text;
diff --git a/third_party/blink/renderer/core/css/media_feature_overrides.cc b/third_party/blink/renderer/core/css/media_feature_overrides.cc
index 36fcd42..ed92912 100644
--- a/third_party/blink/renderer/core/css/media_feature_overrides.cc
+++ b/third_party/blink/renderer/core/css/media_feature_overrides.cc
@@ -89,7 +89,7 @@
   // Document to get the ExecutionContext so the extra parameter should be
   // removed.
   MediaQueryExpBounds bounds =
-      MediaQueryExp::Create(feature, range, *fake_context, nullptr).Bounds();
+      MediaQueryExp::Create(feature, range, *fake_context).Bounds();
   DCHECK(!bounds.left.IsValid());
   MediaQueryExpValue value = bounds.right.value;
 
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 8e174db..f248850a 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/core/css/media_query.h"
 #include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/media_values_dynamic.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
 #include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -553,7 +554,8 @@
                           const MediaValues& media_values,
                           double& result) {
   if (value.IsCSSValue()) {
-    result = value.GetCSSValue().ComputeLength<double>(media_values);
+    result = To<CSSPrimitiveValue>(value.GetCSSValue())
+                 .ComputeLength<double>(media_values);
     return true;
   }
 
@@ -1312,6 +1314,9 @@
   if (!media_values_->BlockSize().has_value() && feature.IsBlockSizeDependent())
     return KleeneValue::kUnknown;
 
+  if (CSSVariableParser::IsValidVariableName(feature.Name()))
+    return EvalStyleFeature(feature, result_flags);
+
   DCHECK(g_function_map);
 
   // Call the media feature evaluation function. Assume no prefix and let
@@ -1347,4 +1352,24 @@
   return result ? KleeneValue::kTrue : KleeneValue::kFalse;
 }
 
+KleeneValue MediaQueryEvaluator::EvalStyleFeature(
+    const MediaQueryFeatureExpNode& feature,
+    MediaQueryResultFlags* result_flags) const {
+  if (!media_values_ || !media_values_->HasValues()) {
+    NOTREACHED()
+        << "media_values has to be initialized for style() container queries";
+    return KleeneValue::kFalse;
+  }
+
+  const MediaQueryExpBounds& bounds = feature.Bounds();
+
+  // Style features always have the form of "property(feature): value".
+  DCHECK(!bounds.IsRange());
+  DCHECK(bounds.right.op == MediaQueryOperator::kNone);
+  DCHECK(bounds.right.IsValid());
+
+  // TODO(crbug.com/1302630): Implement computed style comparison.
+  return KleeneValue::kFalse;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.h b/third_party/blink/renderer/core/css/media_query_evaluator.h
index b0e278a..ebefd57 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.h
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.h
@@ -118,6 +118,8 @@
                      MediaQueryResultFlags*) const;
   KleeneValue EvalFeature(const MediaQueryFeatureExpNode&,
                           MediaQueryResultFlags*) const;
+  KleeneValue EvalStyleFeature(const MediaQueryFeatureExpNode&,
+                               MediaQueryResultFlags*) const;
 
   const String MediaType() const;
 
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index 2583c34..8e2a4a3 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -29,13 +29,15 @@
 
 #include "third_party/blink/renderer/core/css/media_query_exp.h"
 
+#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/core/css/parser/css_tokenized_value.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/decimal.h"
@@ -227,61 +229,6 @@
          media_feature == kMaxDeviceAspectRatioMediaFeature;
 }
 
-static inline bool FeatureWithoutValue(
-    const String& media_feature,
-    const ExecutionContext* execution_context) {
-  // Media features that are prefixed by min/max cannot be used without a value.
-  return media_feature == media_feature_names::kMonochromeMediaFeature ||
-         media_feature == media_feature_names::kColorMediaFeature ||
-         media_feature == media_feature_names::kColorIndexMediaFeature ||
-         media_feature == media_feature_names::kGridMediaFeature ||
-         media_feature == media_feature_names::kHeightMediaFeature ||
-         media_feature == media_feature_names::kWidthMediaFeature ||
-         media_feature == media_feature_names::kBlockSizeMediaFeature ||
-         media_feature == media_feature_names::kInlineSizeMediaFeature ||
-         media_feature == media_feature_names::kDeviceHeightMediaFeature ||
-         media_feature == media_feature_names::kDeviceWidthMediaFeature ||
-         media_feature == media_feature_names::kOrientationMediaFeature ||
-         media_feature == media_feature_names::kAspectRatioMediaFeature ||
-         media_feature == media_feature_names::kDeviceAspectRatioMediaFeature ||
-         media_feature == media_feature_names::kHoverMediaFeature ||
-         media_feature == media_feature_names::kAnyHoverMediaFeature ||
-         media_feature == media_feature_names::kTransform3dMediaFeature ||
-         media_feature == media_feature_names::kPointerMediaFeature ||
-         media_feature == media_feature_names::kAnyPointerMediaFeature ||
-         media_feature == media_feature_names::kDevicePixelRatioMediaFeature ||
-         media_feature == media_feature_names::kResolutionMediaFeature ||
-         media_feature == media_feature_names::kDisplayModeMediaFeature ||
-         media_feature == media_feature_names::kScanMediaFeature ||
-         media_feature == media_feature_names::kColorGamutMediaFeature ||
-         media_feature == media_feature_names::kImmersiveMediaFeature ||
-         media_feature ==
-             media_feature_names::kPrefersColorSchemeMediaFeature ||
-         (media_feature == media_feature_names::kPrefersContrastMediaFeature &&
-          RuntimeEnabledFeatures::PrefersContrastEnabled()) ||
-         media_feature ==
-             media_feature_names::kPrefersReducedMotionMediaFeature ||
-         (media_feature ==
-              media_feature_names::kPrefersReducedDataMediaFeature &&
-          RuntimeEnabledFeatures::PrefersReducedDataEnabled()) ||
-         (media_feature == media_feature_names::kForcedColorsMediaFeature &&
-          RuntimeEnabledFeatures::ForcedColorsEnabled()) ||
-         (media_feature ==
-              media_feature_names::kNavigationControlsMediaFeature &&
-          RuntimeEnabledFeatures::MediaQueryNavigationControlsEnabled()) ||
-         (media_feature == media_feature_names::kOriginTrialTestMediaFeature &&
-          RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabled(
-              execution_context)) ||
-         (media_feature ==
-              media_feature_names::kHorizontalViewportSegmentsMediaFeature &&
-          RuntimeEnabledFeatures::CSSFoldablesEnabled()) ||
-         (media_feature ==
-              media_feature_names::kVerticalViewportSegmentsMediaFeature &&
-          RuntimeEnabledFeatures::CSSFoldablesEnabled()) ||
-         (media_feature == media_feature_names::kDevicePostureMediaFeature &&
-          RuntimeEnabledFeatures::DevicePostureEnabled());
-}
-
 bool MediaQueryExp::IsViewportDependent() const {
   return media_feature_ == media_feature_names::kWidthMediaFeature ||
          media_feature_ == media_feature_names::kHeightMediaFeature ||
@@ -361,30 +308,38 @@
 
 MediaQueryExp MediaQueryExp::Create(const String& media_feature,
                                     CSSParserTokenRange& range,
-                                    const CSSParserContext& context,
-                                    const ExecutionContext* execution_context) {
-  String lower_media_feature =
-      AttemptStaticStringCreation(media_feature.LowerASCII());
-  if (auto value = MediaQueryExpValue::Consume(lower_media_feature, range,
-                                               context, execution_context)) {
-    return MediaQueryExp(lower_media_feature, *value);
+                                    const CSSParserContext& context) {
+  String feature = AttemptStaticStringCreation(media_feature);
+  if (auto value = MediaQueryExpValue::Consume(feature, range, context)) {
+    return MediaQueryExp(feature, *value);
   }
   return Invalid();
 }
 
 absl::optional<MediaQueryExpValue> MediaQueryExpValue::Consume(
-    const String& lower_media_feature,
+    const String& media_feature,
     CSSParserTokenRange& range,
-    const CSSParserContext& context,
-    const ExecutionContext* execution_context) {
-  DCHECK_EQ(lower_media_feature, lower_media_feature.LowerASCII());
-
+    const CSSParserContext& context) {
   CSSParserContext::ParserModeOverridingScope scope(context, kHTMLStandardMode);
 
+  if (CSSVariableParser::IsValidVariableName(media_feature)) {
+    if (const CSSValue* value =
+            CSSVariableParser::ParseDeclarationValue({range}, false, context)) {
+      while (!range.AtEnd())
+        range.Consume();
+      return MediaQueryExpValue(*value);
+    }
+    return absl::nullopt;
+  }
+
+  DCHECK_EQ(media_feature, media_feature.LowerASCII())
+      << "Under the assumption that custom properties in style() container "
+         "queries are currently the only case sensitive features";
+
   CSSPrimitiveValue* value =
       css_parsing_utils::ConsumeInteger(range, context, 0);
-  if (!value && !FeatureExpectingPositiveInteger(lower_media_feature) &&
-      !FeatureWithAspectRatio(lower_media_feature)) {
+  if (!value && !FeatureExpectingPositiveInteger(media_feature) &&
+      !FeatureWithAspectRatio(media_feature)) {
     value = css_parsing_utils::ConsumeNumber(
         range, context, CSSPrimitiveValue::ValueRange::kNonNegative);
   }
@@ -398,20 +353,16 @@
   if (!value) {
     if (CSSIdentifierValue* ident = css_parsing_utils::ConsumeIdent(range)) {
       CSSValueID ident_id = ident->GetValueID();
-      if (!FeatureWithValidIdent(lower_media_feature, ident_id))
+      if (!FeatureWithValidIdent(media_feature, ident_id))
         return absl::nullopt;
       return MediaQueryExpValue(ident_id);
     }
-    if (FeatureWithoutValue(lower_media_feature, execution_context)) {
-      // Valid, creates a MediaQueryExp with an 'invalid' MediaQueryExpValue
-      return MediaQueryExpValue();
-    }
     return absl::nullopt;
   }
 
   // Now we have |value| as a number, length or resolution
   // Create value for media query expression that must have 1 or more values.
-  if (FeatureWithAspectRatio(lower_media_feature)) {
+  if (FeatureWithAspectRatio(media_feature)) {
     if (!value->IsInteger() || value->GetDoubleValue() == 0)
       return absl::nullopt;
     if (!css_parsing_utils::ConsumeSlashIncludingWhitespace(range))
@@ -425,7 +376,7 @@
                               ClampTo<unsigned>(denominator->GetDoubleValue()));
   }
 
-  if (FeatureWithValidDensity(lower_media_feature, value)) {
+  if (FeatureWithValidDensity(media_feature, value)) {
     // TODO(crbug.com/983613): Support resolution in math functions.
     DCHECK(value->IsNumericLiteralValue());
     const auto* numeric_literal = To<CSSNumericLiteralValue>(value);
@@ -433,14 +384,14 @@
                               numeric_literal->GetType());
   }
 
-  if (FeatureWithPositiveInteger(lower_media_feature, value) ||
-      FeatureWithPositiveNumber(lower_media_feature, value) ||
-      FeatureWithZeroOrOne(lower_media_feature, value)) {
+  if (FeatureWithPositiveInteger(media_feature, value) ||
+      FeatureWithPositiveNumber(media_feature, value) ||
+      FeatureWithZeroOrOne(media_feature, value)) {
     return MediaQueryExpValue(value->GetDoubleValue(),
                               CSSPrimitiveValue::UnitType::kNumber);
   }
 
-  if (FeatureWithValidPositiveLength(lower_media_feature, value)) {
+  if (FeatureWithValidPositiveLength(media_feature, value)) {
     if (value->IsNumber()) {
       return MediaQueryExpValue(value->GetDoubleValue(),
                                 CSSPrimitiveValue::UnitType::kNumber);
@@ -499,13 +450,11 @@
 }
 
 String MediaQueryExp::Serialize() const {
-  String name = media_feature_.LowerASCII();
-
   StringBuilder result;
   // <mf-boolean> e.g. (color)
   // <mf-plain>  e.g. (width: 100px)
   if (!bounds_.IsRange()) {
-    result.Append(name);
+    result.Append(media_feature_);
     if (bounds_.right.IsValid()) {
       result.Append(": ");
       result.Append(bounds_.right.value.CssText());
@@ -517,7 +466,7 @@
       result.Append(MediaQueryOperatorToString(bounds_.left.op));
       result.Append(" ");
     }
-    result.Append(name);
+    result.Append(media_feature_);
     if (bounds_.right.IsValid()) {
       result.Append(" ");
       result.Append(MediaQueryOperatorToString(bounds_.right.op));
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h
index 4026e1b..6782c99 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.h
+++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -43,7 +43,6 @@
 
 class CSSParserContext;
 class CSSParserTokenRange;
-class ExecutionContext;
 
 class CORE_EXPORT MediaQueryExpValue {
   DISALLOW_NEW();
@@ -57,7 +56,7 @@
       : type_(Type::kNumeric), numeric_({value, unit}) {}
   MediaQueryExpValue(unsigned numerator, unsigned denominator)
       : type_(Type::kRatio), ratio_({numerator, denominator}) {}
-  explicit MediaQueryExpValue(const CSSPrimitiveValue& value)
+  explicit MediaQueryExpValue(const CSSValue& value)
       : type_(Type::kCSSValue), css_value_(&value) {}
   void Trace(Visitor* visitor) const { visitor->Trace(css_value_); }
 
@@ -92,7 +91,7 @@
     return ratio_.denominator;
   }
 
-  const CSSPrimitiveValue& GetCSSValue() const {
+  const CSSValue& GetCSSValue() const {
     DCHECK(IsCSSValue());
     DCHECK(css_value_);
     return *css_value_;
@@ -137,8 +136,7 @@
   static absl::optional<MediaQueryExpValue> Consume(
       const String& lower_media_feature,
       CSSParserTokenRange&,
-      const CSSParserContext&,
-      const ExecutionContext*);
+      const CSSParserContext&);
 
  private:
   enum class Type { kInvalid, kId, kNumeric, kRatio, kCSSValue };
@@ -158,8 +156,8 @@
   };
 
   // Used when the value can't be represented by the above union (e.g. math
-  // functions).
-  Member<const CSSPrimitiveValue> css_value_;
+  // functions). Also used for style features in style container queries.
+  Member<const CSSValue> css_value_;
 };
 
 // https://drafts.csswg.org/mediaqueries-4/#mq-syntax
@@ -254,8 +252,7 @@
   // Returns an invalid MediaQueryExp if the arguments are invalid.
   static MediaQueryExp Create(const String& media_feature,
                               CSSParserTokenRange&,
-                              const CSSParserContext&,
-                              const ExecutionContext*);
+                              const CSSParserContext&);
   static MediaQueryExp Create(const String& media_feature,
                               const MediaQueryExpBounds&);
   static MediaQueryExp Invalid() {
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser.cc b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
index 1efd698..5837addb 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
@@ -8,7 +8,9 @@
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
+#include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 
@@ -48,24 +50,54 @@
   STACK_ALLOCATED();
 
  public:
-  bool IsAllowed(const String& name) const override {
-    return name == media_feature_names::kWidthMediaFeature ||
-           name == media_feature_names::kMinWidthMediaFeature ||
-           name == media_feature_names::kMaxWidthMediaFeature ||
-           name == media_feature_names::kHeightMediaFeature ||
-           name == media_feature_names::kMinHeightMediaFeature ||
-           name == media_feature_names::kMaxHeightMediaFeature ||
-           name == media_feature_names::kInlineSizeMediaFeature ||
-           name == media_feature_names::kMinInlineSizeMediaFeature ||
-           name == media_feature_names::kMaxInlineSizeMediaFeature ||
-           name == media_feature_names::kBlockSizeMediaFeature ||
-           name == media_feature_names::kMinBlockSizeMediaFeature ||
-           name == media_feature_names::kMaxBlockSizeMediaFeature ||
-           name == media_feature_names::kAspectRatioMediaFeature ||
-           name == media_feature_names::kMinAspectRatioMediaFeature ||
-           name == media_feature_names::kMaxAspectRatioMediaFeature ||
-           name == media_feature_names::kOrientationMediaFeature;
+  bool IsAllowed(const String& feature) const override {
+    return feature == media_feature_names::kWidthMediaFeature ||
+           feature == media_feature_names::kMinWidthMediaFeature ||
+           feature == media_feature_names::kMaxWidthMediaFeature ||
+           feature == media_feature_names::kHeightMediaFeature ||
+           feature == media_feature_names::kMinHeightMediaFeature ||
+           feature == media_feature_names::kMaxHeightMediaFeature ||
+           feature == media_feature_names::kInlineSizeMediaFeature ||
+           feature == media_feature_names::kMinInlineSizeMediaFeature ||
+           feature == media_feature_names::kMaxInlineSizeMediaFeature ||
+           feature == media_feature_names::kBlockSizeMediaFeature ||
+           feature == media_feature_names::kMinBlockSizeMediaFeature ||
+           feature == media_feature_names::kMaxBlockSizeMediaFeature ||
+           feature == media_feature_names::kAspectRatioMediaFeature ||
+           feature == media_feature_names::kMinAspectRatioMediaFeature ||
+           feature == media_feature_names::kMaxAspectRatioMediaFeature ||
+           feature == media_feature_names::kOrientationMediaFeature;
   }
+  bool IsAllowedWithoutValue(const String& feature,
+                             const ExecutionContext*) const override {
+    return feature == media_feature_names::kWidthMediaFeature ||
+           feature == media_feature_names::kHeightMediaFeature ||
+           feature == media_feature_names::kInlineSizeMediaFeature ||
+           feature == media_feature_names::kBlockSizeMediaFeature ||
+           feature == media_feature_names::kAspectRatioMediaFeature ||
+           feature == media_feature_names::kOrientationMediaFeature;
+  }
+  bool IsCaseSensitive(const String& feature) const override { return false; }
+  bool SupportsRange() const override { return true; }
+};
+
+class StyleFeatureSet : public MediaQueryParser::FeatureSet {
+  STACK_ALLOCATED();
+
+ public:
+  bool IsAllowed(const String& feature) const override {
+    // TODO(crbug.com/1302630): Only support querying custom properties for now.
+    return CSSVariableParser::IsValidVariableName(feature);
+  }
+  bool IsAllowedWithoutValue(const String& feature,
+                             const ExecutionContext*) const override {
+    return false;
+  }
+  bool IsCaseSensitive(const String& feature) const override {
+    // TODO(crbug.com/1302630): non-custom properties are case-insensitive.
+    return true;
+  }
+  bool SupportsRange() const override { return false; }
 };
 
 }  // namespace
@@ -100,8 +132,8 @@
     CSSParserTokenRange& range) {
   CSSParserTokenRange original_range = range;
 
-  // ( <size-feature> ) | ( <container-condition> )
   if (range.Peek().GetType() == kLeftParenthesisToken) {
+    // ( <size-feature> ) | ( <container-condition> )
     CSSParserTokenRange block = range.ConsumeBlock();
     block.ConsumeWhitespace();
     range.ConsumeWhitespace();
@@ -117,11 +149,20 @@
     const MediaQueryExpNode* condition = ConsumeContainerCondition(block);
     if (condition && block.AtEnd())
       return MediaQueryExpNode::Nested(condition);
+  } else if (range.Peek().GetType() == kFunctionToken &&
+             range.Peek().FunctionId() == CSSValueID::kStyle) {
+    // style( <style-query> )
+    CSSParserTokenRange block = range.ConsumeBlock();
+    block.ConsumeWhitespace();
+    range.ConsumeWhitespace();
+
+    if (const MediaQueryExpNode* query =
+            ConsumeFeatureQuery(block, StyleFeatureSet())) {
+      return MediaQueryExpNode::Function(query, "style");
+    }
   }
   range = original_range;
 
-  // TODO(crbug.com/1302630): Support style( <style-query> ).
-
   // <general-enclosed>
   return media_query_parser_.ConsumeGeneralEnclosed(range);
 }
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
index bd1eac60..e2011a7 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
@@ -32,9 +32,15 @@
     STACK_ALLOCATED();
 
    public:
-    bool IsAllowed(const String& name) const override {
-      return name == "width";
+    bool IsAllowed(const String& feature) const override {
+      return feature == "width";
     }
+    bool IsAllowedWithoutValue(const String& feature,
+                               const ExecutionContext*) const override {
+      return true;
+    }
+    bool IsCaseSensitive(const String& feature) const override { return false; }
+    bool SupportsRange() const override { return true; }
   };
 
   // E.g. https://drafts.csswg.org/css-contain-3/#typedef-style-query
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.cc b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
index 0f79f09..70b894c 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/media_type_names.h"
@@ -41,6 +42,60 @@
     }
     return true;
   }
+  bool IsAllowedWithoutValue(
+      const String& feature,
+      const ExecutionContext* execution_context) const override {
+    // Media features that are prefixed by min/max cannot be used without a
+    // value.
+    return feature == media_feature_names::kMonochromeMediaFeature ||
+           feature == media_feature_names::kColorMediaFeature ||
+           feature == media_feature_names::kColorIndexMediaFeature ||
+           feature == media_feature_names::kGridMediaFeature ||
+           feature == media_feature_names::kHeightMediaFeature ||
+           feature == media_feature_names::kWidthMediaFeature ||
+           feature == media_feature_names::kBlockSizeMediaFeature ||
+           feature == media_feature_names::kInlineSizeMediaFeature ||
+           feature == media_feature_names::kDeviceHeightMediaFeature ||
+           feature == media_feature_names::kDeviceWidthMediaFeature ||
+           feature == media_feature_names::kOrientationMediaFeature ||
+           feature == media_feature_names::kAspectRatioMediaFeature ||
+           feature == media_feature_names::kDeviceAspectRatioMediaFeature ||
+           feature == media_feature_names::kHoverMediaFeature ||
+           feature == media_feature_names::kAnyHoverMediaFeature ||
+           feature == media_feature_names::kTransform3dMediaFeature ||
+           feature == media_feature_names::kPointerMediaFeature ||
+           feature == media_feature_names::kAnyPointerMediaFeature ||
+           feature == media_feature_names::kDevicePixelRatioMediaFeature ||
+           feature == media_feature_names::kResolutionMediaFeature ||
+           feature == media_feature_names::kDisplayModeMediaFeature ||
+           feature == media_feature_names::kScanMediaFeature ||
+           feature == media_feature_names::kColorGamutMediaFeature ||
+           feature == media_feature_names::kImmersiveMediaFeature ||
+           feature == media_feature_names::kPrefersColorSchemeMediaFeature ||
+           (feature == media_feature_names::kPrefersContrastMediaFeature &&
+            RuntimeEnabledFeatures::PrefersContrastEnabled()) ||
+           feature == media_feature_names::kPrefersReducedMotionMediaFeature ||
+           (feature == media_feature_names::kPrefersReducedDataMediaFeature &&
+            RuntimeEnabledFeatures::PrefersReducedDataEnabled()) ||
+           (feature == media_feature_names::kForcedColorsMediaFeature &&
+            RuntimeEnabledFeatures::ForcedColorsEnabled()) ||
+           (feature == media_feature_names::kNavigationControlsMediaFeature &&
+            RuntimeEnabledFeatures::MediaQueryNavigationControlsEnabled()) ||
+           (feature == media_feature_names::kOriginTrialTestMediaFeature &&
+            RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabled(
+                execution_context)) ||
+           (feature ==
+                media_feature_names::kHorizontalViewportSegmentsMediaFeature &&
+            RuntimeEnabledFeatures::CSSFoldablesEnabled()) ||
+           (feature ==
+                media_feature_names::kVerticalViewportSegmentsMediaFeature &&
+            RuntimeEnabledFeatures::CSSFoldablesEnabled()) ||
+           (feature == media_feature_names::kDevicePostureMediaFeature &&
+            RuntimeEnabledFeatures::DevicePostureEnabled());
+  }
+
+  bool IsCaseSensitive(const String& feature) const override { return false; }
+  bool SupportsRange() const override { return true; }
 
  private:
   CSSParserMode parser_mode_;
@@ -192,8 +247,10 @@
                                             const FeatureSet& feature_set) {
   if (range.Peek().GetType() != kIdentToken)
     return g_null_atom;
-  String name =
-      AttemptStaticStringCreation(range.Peek().Value().ToString().LowerASCII());
+  String name = range.Peek().Value().ToString();
+  if (!feature_set.IsCaseSensitive(name))
+    name = name.LowerASCII();
+  name = AttemptStaticStringCreation(name);
   if (!feature_set.IsAllowed(name))
     return g_null_atom;
   range.ConsumeIncludingWhitespace();
@@ -205,7 +262,6 @@
   String name = ConsumeAllowedName(range, feature_set);
   if (name.IsNull())
     return name;
-  DCHECK_EQ(name, name.LowerASCII());
   if (name.StartsWith("min-") || name.StartsWith("max-"))
     return g_null_atom;
   return name;
@@ -224,8 +280,7 @@
   if (feature_name.IsNull() || !lhs.AtEnd())
     return nullptr;
 
-  auto value = MediaQueryExpValue::Consume(feature_name, rhs, fake_context_,
-                                           execution_context_);
+  auto value = MediaQueryExpValue::Consume(feature_name, rhs, fake_context_);
 
   if (!value || !rhs.AtEnd())
     return nullptr;
@@ -261,31 +316,27 @@
   // <mf-boolean> = <mf-name>
   if (range.AtEnd()) {
     String feature_name = ConsumeAllowedName(segment1, feature_set);
-    if (feature_name.IsNull() || !segment1.AtEnd())
+    if (feature_name.IsNull() || !segment1.AtEnd() ||
+        !feature_set.IsAllowedWithoutValue(feature_name, execution_context_)) {
       return nullptr;
-    auto exp = MediaQueryExp::Create(feature_name, range, fake_context_,
-                                     execution_context_);
-    if (!exp.IsValid())
-      return nullptr;
-    return MakeGarbageCollected<MediaQueryFeatureExpNode>(exp);
+    }
+    return MakeGarbageCollected<MediaQueryFeatureExpNode>(
+        MediaQueryExp::Create(feature_name, MediaQueryExpBounds()));
   }
 
   // <mf-plain> = <mf-name> : <mf-value>
   if (range.Peek().GetType() == kColonToken) {
     range.ConsumeIncludingWhitespace();
-    if (range.AtEnd())
-      return nullptr;
     String feature_name = ConsumeAllowedName(segment1, feature_set);
     if (feature_name.IsNull() || !segment1.AtEnd())
       return nullptr;
-    auto exp = MediaQueryExp::Create(feature_name, range, fake_context_,
-                                     execution_context_);
+    auto exp = MediaQueryExp::Create(feature_name, range, fake_context_);
     if (!exp.IsValid() || !range.AtEnd())
       return nullptr;
     return MakeGarbageCollected<MediaQueryFeatureExpNode>(exp);
   }
 
-  if (!IsMediaQueries4SyntaxEnabled())
+  if (!IsMediaQueries4SyntaxEnabled() || !feature_set.SupportsRange())
     return nullptr;
 
   // Otherwise <mf-range>:
@@ -342,14 +393,14 @@
   if (feature_name.IsNull() || !segment2.AtEnd())
     return nullptr;
 
-  auto left_value = MediaQueryExpValue::Consume(
-      feature_name, segment1, fake_context_, execution_context_);
+  auto left_value =
+      MediaQueryExpValue::Consume(feature_name, segment1, fake_context_);
   if (!left_value || !segment1.AtEnd())
     return nullptr;
 
   CSSParserTokenRange& segment3 = range;
-  auto right_value = MediaQueryExpValue::Consume(
-      feature_name, segment3, fake_context_, execution_context_);
+  auto right_value =
+      MediaQueryExpValue::Consume(feature_name, segment3, fake_context_);
   if (!right_value || !segment3.AtEnd())
     return nullptr;
 
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.h b/third_party/blink/renderer/core/css/parser/media_query_parser.h
index a89eddb..4492006 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.h
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.h
@@ -39,7 +39,19 @@
     STACK_ALLOCATED();
 
    public:
-    virtual bool IsAllowed(const String&) const = 0;
+    // Returns true if the feature name is allowed in this set.
+    virtual bool IsAllowed(const String& feature) const = 0;
+
+    // Returns true if the feature can be queried without a value.
+    virtual bool IsAllowedWithoutValue(const String& feature,
+                                       const ExecutionContext*) const = 0;
+
+    // Returns true is the feature name is case sensitive.
+    virtual bool IsCaseSensitive(const String& feature) const = 0;
+
+    // Whether the features support range syntax. This is typically false for
+    // style container queries.
+    virtual bool SupportsRange() const = 0;
   };
 
  private:
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 6722eea6..a4df189 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -2100,7 +2100,7 @@
   if (!rules)
     return;
   for (auto rule : *rules)
-    state.Style()->AddCallbackSelector(rule->SelectorList().SelectorsText());
+    state.Style()->AddCallbackSelector(rule->SelectorsText());
 }
 
 // Font properties are also handled by FontStyleResolver outside the main
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 0d6316a..918d80d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -64,8 +64,9 @@
       element_type_(style_request.IsPseudoStyleRequest()
                         ? ElementType::kPseudoElement
                         : ElementType::kElement),
-      container_unit_context_(
-          style_recalc_context ? style_recalc_context->container : &element),
+      container_unit_context_(style_recalc_context
+                                  ? style_recalc_context->container
+                                  : element.ParentOrShadowHostElement()),
       originating_element_style_(style_request.originating_element_style),
       is_for_highlight_(IsHighlightPseudoElement(style_request.pseudo_id)),
       is_for_custom_highlight_(style_request.pseudo_id ==
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index 3bb0382..1e87534 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -2866,4 +2866,38 @@
   UpdateAllLifecyclePhasesForTest();
 }
 
+TEST_F(StyleResolverTestCQ, ContainerUnitContext) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #container, #div { container-type:size; }
+      #container {
+        width: 200px;
+        height: 200px;
+      }
+      #div {
+        width: 100px;
+        height: 100px;
+      }
+    </style>
+    <div id="container">
+      <div id="div"></div>
+    </div>
+  )HTML");
+
+  Element* div = GetDocument().getElementById("div");
+  ASSERT_TRUE(div);
+
+  scoped_refptr<ComputedStyle> style =
+      ComputedStyle::Clone(div->ComputedStyleRef());
+
+  // Don't provide a StyleRecalcContext here.
+  StyleResolverState state(GetDocument(), *div);
+
+  // To make UpdateLengthConversionData happen.
+  state.SetStyle(style);
+
+  EXPECT_DOUBLE_EQ(200.0, state.CssToLengthConversionData().ContainerWidth());
+  EXPECT_DOUBLE_EQ(200.0, state.CssToLengthConversionData().ContainerHeight());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/rule_feature_set_test.cc b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
index 2e3d515..9d388fe 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set_test.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set_test.cc
@@ -47,20 +47,26 @@
       CSSSelectorList selector_list,
       const StyleScope* style_scope,
       RuleFeatureSet& set) {
-    Vector<wtf_size_t> indices;
-    for (const CSSSelector* s = selector_list.First(); s;
-         s = selector_list.Next(*s)) {
-      indices.push_back(selector_list.SelectorIndex(*s));
-    }
-
     auto* style_rule = MakeGarbageCollected<StyleRule>(
         std::move(selector_list),
         MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode));
+    return CollectFeaturesTo(style_rule, style_scope, set);
+  }
+
+  static RuleFeatureSet::SelectorPreMatch CollectFeaturesTo(
+      StyleRule* style_rule,
+      const StyleScope* style_scope,
+      RuleFeatureSet& set) {
+    Vector<wtf_size_t> indices;
+    for (const CSSSelector* s = style_rule->FirstSelector(); s;
+         s = CSSSelectorList::Next(*s)) {
+      indices.push_back(style_rule->SelectorIndex(*s));
+    }
 
     RuleFeatureSet::SelectorPreMatch result =
         RuleFeatureSet::SelectorPreMatch::kSelectorNeverMatches;
-    for (unsigned i = 0; i < indices.size(); ++i) {
-      RuleData rule_data(style_rule, indices[i], 0, 0, kRuleHasNoSpecialState);
+    for (wtf_size_t index : indices) {
+      RuleData rule_data(style_rule, index, 0, 0, kRuleHasNoSpecialState);
       if (set.CollectFeaturesFromRuleData(&rule_data, style_scope))
         result = RuleFeatureSet::SelectorPreMatch::kSelectorMayMatch;
     }
@@ -1813,9 +1819,7 @@
     auto* style_rule = DynamicTo<StyleRule>(rule);
     ASSERT_TRUE(style_rule);
 
-    DCHECK(style_rule->SelectorList().IsValid());
-
-    CollectFeaturesTo(style_rule->SelectorList().Copy(), scope, set);
+    CollectFeaturesTo(style_rule, scope, set);
   }
 
   void Compare(const RuleFeatureSet& main,
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 5f1dedc..21334606 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -489,10 +489,9 @@
     StyleRuleBase* rule = rules[i].Get();
 
     if (auto* style_rule = DynamicTo<StyleRule>(rule)) {
-      const CSSSelectorList& selector_list = style_rule->SelectorList();
-      for (const CSSSelector* selector = selector_list.First(); selector;
-           selector = selector_list.Next(*selector)) {
-        wtf_size_t selector_index = selector_list.SelectorIndex(*selector);
+      for (const CSSSelector* selector = style_rule->FirstSelector(); selector;
+           selector = CSSSelectorList::Next(*selector)) {
+        wtf_size_t selector_index = style_rule->SelectorIndex(*selector);
         AddRule(style_rule, selector_index, add_rule_flags, container_query,
                 cascade_layer, style_scope);
       }
@@ -609,11 +608,8 @@
 }
 
 void RuleSet::AddStyleRule(StyleRule* rule, AddRuleFlags add_rule_flags) {
-  for (wtf_size_t selector_index =
-           rule->SelectorList().SelectorIndex(*rule->SelectorList().First());
-       selector_index != kNotFound;
-       selector_index =
-           rule->SelectorList().IndexOfNextSelectorAfter(selector_index)) {
+  for (wtf_size_t selector_index = 0; selector_index != kNotFound;
+       selector_index = rule->IndexOfNextSelectorAfter(selector_index)) {
     AddRule(rule, selector_index, add_rule_flags, nullptr /* container_query */,
             nullptr /* cascade_layer */, nullptr /* scope */);
   }
diff --git a/third_party/blink/renderer/core/css/rule_set.h b/third_party/blink/renderer/core/css/rule_set.h
index 94e7f2dc4..3d9dcef 100644
--- a/third_party/blink/renderer/core/css/rule_set.h
+++ b/third_party/blink/renderer/core/css/rule_set.h
@@ -108,7 +108,7 @@
   unsigned GetPosition() const { return position_; }
   StyleRule* Rule() const { return rule_; }
   const CSSSelector& Selector() const {
-    return rule_->SelectorList().SelectorAt(selector_index_);
+    return rule_->SelectorAt(selector_index_);
   }
   unsigned SelectorIndex() const { return selector_index_; }
 
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 06f365d..7153823 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -243,7 +243,7 @@
 
   if (sub_result.dynamic_pseudo != kPseudoIdNone) {
     result.dynamic_pseudo = sub_result.dynamic_pseudo;
-    result.custom_highlight_name = sub_result.custom_highlight_name;
+    result.custom_highlight_name = std::move(sub_result.custom_highlight_name);
   }
 
   if (context.selector->IsLastInTagHistory())
diff --git a/third_party/blink/renderer/core/css/selector_checker_test.cc b/third_party/blink/renderer/core/css/selector_checker_test.cc
index 0a32efcd..ff738d9 100644
--- a/third_party/blink/renderer/core/css/selector_checker_test.cc
+++ b/third_party/blink/renderer/core/css/selector_checker_test.cc
@@ -156,8 +156,7 @@
 
   auto* style_rule = DynamicTo<StyleRule>(rule);
   ASSERT_TRUE(style_rule);
-  ASSERT_TRUE(style_rule->SelectorList().IsValid());
-  ASSERT_EQ(1u, style_rule->SelectorList().ComputeLength());
+  ASSERT_TRUE(style_rule->FirstSelector()->IsLastInSelectorList());
 
   Element* target = GetDocument().getElementById("target");
   ASSERT_TRUE(target);
@@ -165,7 +164,7 @@
   SelectorChecker checker(SelectorChecker::kResolvingStyle);
   SelectorChecker::StyleScopeFrame style_scope_frame(*target);
   SelectorChecker::SelectorCheckingContext context(target);
-  context.selector = style_rule->SelectorList().First();
+  context.selector = style_rule->FirstSelector();
   context.style_scope = scope;
   context.style_scope_frame = &style_scope_frame;
 
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h
index 50d0c1b..3b203bc3 100644
--- a/third_party/blink/renderer/core/css/style_rule.h
+++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -128,7 +128,25 @@
   StyleRule(CSSSelectorList, CSSLazyPropertyParser*);
   StyleRule(const StyleRule&);
 
-  const CSSSelectorList& SelectorList() const { return selector_list_; }
+  // Partial subset of the CSSSelector API.
+  const CSSSelector* FirstSelector() const { return selector_list_.First(); }
+  const CSSSelector& SelectorAt(wtf_size_t index) const {
+    return selector_list_.SelectorAt(index);
+  }
+  wtf_size_t SelectorIndex(const CSSSelector& selector) const {
+    return static_cast<wtf_size_t>(&selector - FirstSelector());
+  }
+  wtf_size_t IndexOfNextSelectorAfter(wtf_size_t index) const {
+    const CSSSelector& current = SelectorAt(index);
+    const CSSSelector* next = CSSSelectorList::Next(current);
+    if (!next)
+      return kNotFound;
+    return SelectorIndex(*next);
+  }
+  String SelectorsText() const {
+    return CSSSelectorList::SelectorsText(FirstSelector());
+  }
+
   const CSSPropertyValueSet& Properties() const;
   MutableCSSPropertyValueSet& MutableProperties();
 
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index bada019..80629d5 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -405,8 +405,8 @@
   // Reports first usage of `navigator.userAgent` and related getters
   void ReportNavigatorUserAgentAccess();
 
-  virtual ukm::UkmRecorder* UkmRecorder() { return nullptr; }
-  virtual ukm::SourceId UkmSourceID() const { return ukm::kInvalidSourceId; }
+  virtual ukm::UkmRecorder* UkmRecorder() = 0;
+  virtual ukm::SourceId UkmSourceID() const = 0;
 
   // Returns the token that uniquely identifies this ExecutionContext.
   virtual ExecutionContextToken GetExecutionContextToken() const = 0;
diff --git a/third_party/blink/renderer/core/html/parser/atomic_html_token.cc b/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
index 0aa0405..1bc6d194 100644
--- a/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
+++ b/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
@@ -6,10 +6,7 @@
 
 namespace blink {
 
-QualifiedName AtomicHTMLToken::NameForAttribute(
-    const HTMLToken::Attribute& attribute) const {
-  return QualifiedName(g_null_atom, attribute.GetName(), g_null_atom);
-}
+bool g_use_html_attribute_name_lookup = true;
 
 bool AtomicHTMLToken::UsesName() const {
   return type_ == HTMLToken::kStartTag || type_ == HTMLToken::kEndTag ||
diff --git a/third_party/blink/renderer/core/html/parser/atomic_html_token.h b/third_party/blink/renderer/core/html/parser/atomic_html_token.h
index 8a6391c..e20b773 100644
--- a/third_party/blink/renderer/core/html/parser/atomic_html_token.h
+++ b/third_party/blink/renderer/core/html/parser/atomic_html_token.h
@@ -38,8 +38,16 @@
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
+// TODO(https://crbug.com/1338583): enable on android.
+#if !BUILDFLAG(IS_ANDROID)
+#include "third_party/blink/renderer/core/html_element_attribute_name_lookup_trie.h"  // nogncheck
+#endif
+
 namespace blink {
 
+// Controls whether attribute name lookup uses LookupHTMLAttributeName().
+CORE_EXPORT extern bool g_use_html_attribute_name_lookup;
+
 class CORE_EXPORT AtomicHTMLToken {
   STACK_ALLOCATED();
 
@@ -159,7 +167,6 @@
   HTMLToken::TokenType type_;
 
   void InitializeAttributes(const HTMLToken::AttributeList& attributes);
-  QualifiedName NameForAttribute(const HTMLToken::Attribute&) const;
 
   bool UsesName() const;
 
@@ -193,7 +200,8 @@
   HashSet<AtomicString> added_attributes;
   added_attributes.ReserveCapacityForSize(size);
 
-  attributes_.clear();
+  // This is only called once, so `attributes_` should be empty.
+  DCHECK(attributes_.IsEmpty());
   attributes_.ReserveInitialCapacity(size);
   for (const auto& attribute : attributes) {
     if (attribute.NameIsEmpty())
@@ -204,19 +212,30 @@
     attribute.ValueRange().CheckValid();
 #endif
 
+    QualifiedName name = g_null_name;
+#if !BUILDFLAG(IS_ANDROID)
+    if (g_use_html_attribute_name_lookup) {
+      name = LookupHTMLAttributeName(attribute.NameBuffer().data(),
+                                     attribute.NameBuffer().size());
+    }
+#endif
+    if (name == g_null_name) {
+      name = QualifiedName(g_null_atom, attribute.GetName(), g_null_atom);
+    }
+
+    if (!added_attributes.insert(name.LocalName()).is_new_entry) {
+      duplicate_attribute_ = true;
+      continue;
+    }
+
     // The string pointer in |value| is null for attributes with no values, but
     // the null atom is used to represent absence of attributes; attributes with
     // no values have the value set to an empty atom instead.
-    QualifiedName name = NameForAttribute(attribute);
-    if (added_attributes.insert(name.LocalName()).is_new_entry) {
-      AtomicString value(attribute.GetValue());
-      if (value.IsNull()) {
-        value = g_empty_atom;
-      }
-      attributes_.UncheckedAppend(Attribute(std::move(name), std::move(value)));
-    } else {
-      duplicate_attribute_ = true;
+    AtomicString value(attribute.GetValue());
+    if (value.IsNull()) {
+      value = g_empty_atom;
     }
+    attributes_.UncheckedAppend(Attribute(std::move(name), std::move(value)));
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/parser/html_token.h b/third_party/blink/renderer/core/html/parser/html_token.h
index 8d9fd21..5d0eed0 100644
--- a/third_party/blink/renderer/core/html/parser/html_token.h
+++ b/third_party/blink/renderer/core/html/parser/html_token.h
@@ -117,6 +117,8 @@
     AtomicString GetName() const { return name_.AsAtomicString(); }
     AtomicString GetValue() const { return value_.AsAtomicString(); }
 
+    const UCharLiteralBuffer<32>& NameBuffer() const { return name_; }
+
     String NameAttemptStaticStringCreation() const {
       return AttemptStaticStringCreation(name_, kLikely8Bit);
     }
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 88292b54..3b7fdb5 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -2401,19 +2401,19 @@
     //  (complex): {.foo: 0, .bar: 1, h1: 2, body: 3, h1: 4}, matches: [2, 4]
     // (compound): {.foo: 0, .bar: 0, h1: 1, body: 2, h1: 2}, matches: [1, 2]
     auto matching_selectors = std::make_unique<protocol::Array<int>>();
-    if (rule->GetStyleRule() && rule->GetStyleRule()->SelectorList().First()) {
-      const CSSSelectorList& list = rule->GetStyleRule()->SelectorList();
+    if (rule->GetStyleRule()) {
       // Compound index (0 -> 1 -> 2).
       int compound = 0;
       // Complex index of the next compound (0 -> 2 -> 3 -> kNotFound).
-      wtf_size_t next_compound_start = list.IndexOfNextSelectorAfter(0);
+      wtf_size_t next_compound_start =
+          rule->GetStyleRule()->IndexOfNextSelectorAfter(0);
 
       std::sort(rule_indices.at(rule)->begin(), rule_indices.at(rule)->end());
       for (unsigned complex_match : (*rule_indices.at(rule))) {
         while (complex_match >= next_compound_start &&
                next_compound_start != kNotFound) {
-          next_compound_start =
-              list.IndexOfNextSelectorAfter(next_compound_start);
+          next_compound_start = rule->GetStyleRule()->IndexOfNextSelectorAfter(
+              next_compound_start);
           compound++;
         }
         matching_selectors->push_back(compound);
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index a2cfa98..968f558 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -139,12 +139,8 @@
 }
 
 void GetClassNamesFromRule(CSSStyleRule* rule, HashSet<String>& unique_names) {
-  const CSSSelectorList& selector_list = rule->GetStyleRule()->SelectorList();
-  if (!selector_list.IsValid())
-    return;
-
-  for (const CSSSelector* sub_selector = selector_list.First(); sub_selector;
-       sub_selector = CSSSelectorList::Next(*sub_selector)) {
+  for (const CSSSelector* sub_selector = rule->GetStyleRule()->FirstSelector();
+       sub_selector; sub_selector = CSSSelectorList::Next(*sub_selector)) {
     const CSSSelector* simple_selector = sub_selector;
     while (simple_selector) {
       if (simple_selector->Match() == CSSSelector::kClass)
@@ -1794,9 +1790,8 @@
     selectors = SelectorsFromSource(source_data, text_);
   } else {
     selectors = std::make_unique<protocol::Array<protocol::CSS::Value>>();
-    const CSSSelectorList& selector_list = rule->GetStyleRule()->SelectorList();
-    for (const CSSSelector* selector = selector_list.First(); selector;
-         selector = CSSSelectorList::Next(*selector)) {
+    for (const CSSSelector* selector = rule->GetStyleRule()->FirstSelector();
+         selector; selector = CSSSelectorList::Next(*selector)) {
       selectors->emplace_back(protocol::CSS::Value::create()
                                   .setText(selector->SelectorText())
                                   .build());
diff --git a/third_party/blink/renderer/core/svg/svg_element_test.cc b/third_party/blink/renderer/core/svg/svg_element_test.cc
index 3b8bb14..673c08d 100644
--- a/third_party/blink/renderer/core/svg/svg_element_test.cc
+++ b/third_party/blink/renderer/core/svg/svg_element_test.cc
@@ -4,9 +4,11 @@
 
 #include "third_party/blink/renderer/core/svg/svg_element.h"
 
+#include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/svg/svg_element_rare_data.h"
+#include "third_party/blink/renderer/core/svg/svg_length_context.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 
 namespace blink {
@@ -68,4 +70,33 @@
             Color::kTransparent);
 }
 
+TEST_F(SVGElementTest, ContainerUnitContext) {
+  ScopedCSSContainerQueriesForTest scoped_cq(true);
+  ScopedCSSContainerSkipStyleRecalcForTest scoped_skip(true);
+  ScopedLayoutNGForTest scoped_ng(true);
+
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #container, #svg { container-type:size; }
+      #container {
+        width: 200px;
+        height: 200px;
+      }
+      #svg {
+        width: 100px;
+        height: 100px;
+      }
+    </style>
+    <div id="container">
+      <svg id="svg"></svg>
+    </div>
+  )HTML");
+
+  auto* svg = To<SVGElement>(GetDocument().getElementById("svg"));
+  const auto* value = DynamicTo<CSSPrimitiveValue>(
+      css_test_helpers::ParseValue(GetDocument(), "<length>", "100cqw"));
+  EXPECT_FLOAT_EQ(200.0f, SVGLengthContext(svg).ResolveValue(
+                              *value, SVGLengthMode::kWidth));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/svg_length_context.cc b/third_party/blink/renderer/core/svg/svg_length_context.cc
index 63fd295..7671162 100644
--- a/third_party/blink/renderer/core/svg/svg_length_context.cc
+++ b/third_party/blink/renderer/core/svg/svg_length_context.cc
@@ -512,7 +512,9 @@
   DCHECK(context_);
   CSSToLengthConversionData conversion_data = CSSToLengthConversionData(
       style, root_style, context_->GetDocument().GetLayoutView(),
-      CSSToLengthConversionData::ContainerSizes(context_), 1.0f);
+      CSSToLengthConversionData::ContainerSizes(
+          context_->ParentOrShadowHostElement()),
+      1.0f);
   Length length = primitive_value.ConvertToLength(conversion_data);
   return ValueForLength(length, 1.0f, mode);
 }
diff --git a/third_party/blink/renderer/core/testing/null_execution_context.h b/third_party/blink/renderer/core/testing/null_execution_context.h
index 699b2d0..2f0e2ab 100644
--- a/third_party/blink/renderer/core/testing/null_execution_context.h
+++ b/third_party/blink/renderer/core/testing/null_execution_context.h
@@ -50,6 +50,8 @@
   ResourceFetcher* Fetcher() override { return nullptr; }
   bool CrossOriginIsolatedCapability() const override { return false; }
   bool DirectSocketCapability() const override { return false; }
+  ukm::UkmRecorder* UkmRecorder() override { return nullptr; }
+  ukm::SourceId UkmSourceID() const override { return ukm::kInvalidSourceId; }
   FrameOrWorkerScheduler* GetScheduler() override;
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override;
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index 1889af0..7003eed 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -324,6 +324,10 @@
   return ukm_recorder_.get();
 }
 
+ukm::SourceId WorkletGlobalScope::UkmSourceID() const {
+  return ukm::kInvalidSourceId;
+}
+
 void WorkletGlobalScope::Trace(Visitor* visitor) const {
   visitor->Trace(frame_);
   WorkerOrWorkletGlobalScope::Trace(visitor);
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
index c421276e..e5b63822 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
@@ -69,6 +69,7 @@
   bool CrossOriginIsolatedCapability() const final;
   bool DirectSocketCapability() const final;
   ukm::UkmRecorder* UkmRecorder() final;
+  ukm::SourceId UkmSourceID() const final;
 
   // WorkerOrWorkletGlobalScope
   void Dispose() override;
diff --git a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
index 01f79d4..e3ad66f 100644
--- a/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
+++ b/third_party/blink/renderer/modules/mediastream/webmediaplayer_ms.cc
@@ -1084,13 +1084,16 @@
 void WebMediaPlayerMS::OnFrameHidden() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (watch_time_reporter_)
+  bool in_picture_in_picture =
+      client_->GetDisplayType() == DisplayType::kPictureInPicture;
+
+  if (watch_time_reporter_ && !in_picture_in_picture)
     watch_time_reporter_->OnHidden();
 
   // This method is called when the RenderFrame is sent to background or
   // suspended. During undoable tab closures OnHidden() may be called back to
   // back, so we can't rely on |render_frame_suspended_| being false here.
-  if (frame_deliverer_) {
+  if (frame_deliverer_ && !in_picture_in_picture) {
     PostCrossThreadTask(
         *io_task_runner_, FROM_HERE,
         CrossThreadBindOnce(&FrameDeliverer::SetRenderFrameSuspended,
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index f8c6d977..7f82ce7 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -143,8 +143,11 @@
         frame->Client()->MediaStreamDeviceObserver();
     if (!media_stream_device_observer)
       return;
-
-    bool suspend = !GetPage()->IsPageVisible();
+    // Don't suspend media capture devices if page visibility is
+    // PageVisibilityState::kHiddenButPainting (e.g. Picture-in-Picture).
+    // TODO(crbug.com/1339252): Add tests.
+    bool suspend = (GetPage()->GetVisibilityState() ==
+                    mojom::blink::PageVisibilityState::kHidden);
     MediaStreamDevices video_devices =
         media_stream_device_observer->GetNonScreenCaptureDevices();
     Platform::Current()->GetVideoCaptureImplManager()->SuspendDevices(
diff --git a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
index 586d74c..6391fcc 100644
--- a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
+++ b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.cc
@@ -36,6 +36,12 @@
     const NotificationOptions* options,
     ExceptionState& exception_state) {
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  if (execution_context->IsInFencedFrame()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "showNotification() is not allowed in fenced frames.");
+    return ScriptPromise();
+  }
 
   // If context object's active worker is null, reject the promise with a
   // TypeError exception.
diff --git a/third_party/blink/renderer/modules/push_messaging/push_manager.cc b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
index 826388d..ee36b535 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_manager.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_manager.cc
@@ -73,6 +73,15 @@
                                       "Window is detached.");
     return ScriptPromise();
   }
+
+  ExecutionContext* execution_context = ExecutionContext::From(script_state);
+  if (execution_context->IsInFencedFrame()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "subscribe() is not allowed in fenced frames.");
+    return ScriptPromise();
+  }
+
   if (!registration_->active()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kAbortError,
diff --git a/third_party/blink/renderer/modules/sanitizer_api/builtins.cc b/third_party/blink/renderer/modules/sanitizer_api/builtins.cc
index 62f137f..7d6ca3b 100644
--- a/third_party/blink/renderer/modules/sanitizer_api/builtins.cc
+++ b/third_party/blink/renderer/modules/sanitizer_api/builtins.cc
@@ -21,17 +21,6 @@
 // arguments. Thus we have this typedef as a work-around.
 typedef HashMap<String, String> StringMap;
 
-StringMap MixedCaseNames(const char* const* names) {
-  HashMap<String, String> map;
-  for (const char* const* iter = names; *iter; ++iter) {
-    String name(*iter);
-    if (!name.IsLowerASCII()) {
-      map.insert(name.LowerASCII(), name);
-    }
-  }
-  return map;
-}
-
 SanitizerConfigImpl::ElementList ElementsFromAPI(const char* const* elements) {
   SanitizerConfigImpl::ElementList element_list;
   for (const char* const* elem = elements; *elem; ++elem) {
@@ -91,18 +80,6 @@
   return attributes_;
 }
 
-const HashMap<String, String>& GetMixedCaseElementNames() {
-  DEFINE_STATIC_LOCAL(StringMap, element_names_,
-                      (MixedCaseNames(kBaselineElements)));
-  return element_names_;
-}
-
-const HashMap<String, String>& GetMixedCaseAttributeNames() {
-  DEFINE_STATIC_LOCAL(StringMap, attribute_names_,
-                      (MixedCaseNames(kBaselineAttributes)));
-  return attribute_names_;
-}
-
 }  // namespace default_config_names
 
 namespace with_namespace_names {
@@ -126,18 +103,6 @@
   return attributes_;
 }
 
-const HashMap<String, String>& GetMixedCaseElementNames() {
-  DEFINE_STATIC_LOCAL(StringMap, element_names_,
-                      (MixedCaseNames(kBaselineElements)));
-  return element_names_;
-}
-
-const HashMap<String, String>& GetMixedCaseAttributeNames() {
-  DEFINE_STATIC_LOCAL(StringMap, attribute_names_,
-                      (MixedCaseNames(kBaselineAttributes)));
-  return attribute_names_;
-}
-
 }  // namespace with_namespace_names
 
 bool WithNamespaces() {
@@ -161,15 +126,4 @@
   return WithNamespaces() ? with_namespace_names::GetBaselineAllowAttributes()
                           : default_config_names::GetBaselineAllowAttributes();
 }
-
-const HashMap<String, String>& GetMixedCaseElementNames() {
-  return WithNamespaces() ? with_namespace_names::GetMixedCaseElementNames()
-                          : default_config_names::GetMixedCaseElementNames();
-}
-
-const HashMap<String, String>& GetMixedCaseAttributeNames() {
-  return WithNamespaces() ? with_namespace_names::GetMixedCaseAttributeNames()
-                          : default_config_names::GetMixedCaseAttributeNames();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/sanitizer_api/builtins.h b/third_party/blink/renderer/modules/sanitizer_api/builtins.h
index be5ddcd3..4b6521d 100644
--- a/third_party/blink/renderer/modules/sanitizer_api/builtins.h
+++ b/third_party/blink/renderer/modules/sanitizer_api/builtins.h
@@ -19,11 +19,6 @@
 const SanitizerConfigImpl::ElementList& GetBaselineAllowElements();
 const SanitizerConfigImpl::AttributeList& GetBaselineAllowAttributes();
 
-// We derive a map of lower-case to mixed-case names from the built-ins, for
-// use in name normalization.
-const HashMap<String, String>& GetMixedCaseElementNames();
-const HashMap<String, String>& GetMixedCaseAttributeNames();
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SANITIZER_API_BUILTINS_H_
diff --git a/third_party/blink/renderer/modules/sanitizer_api/config_util.cc b/third_party/blink/renderer/modules/sanitizer_api/config_util.cc
index 605a9f23..c103ef5 100644
--- a/third_party/blink/renderer/modules/sanitizer_api/config_util.cc
+++ b/third_party/blink/renderer/modules/sanitizer_api/config_util.cc
@@ -88,6 +88,7 @@
   //     obtuse, but it seems to allow all XML names. The HTML parser however
   //     allows only ascii. Here, we settle for the simplest, most restrictive
   //     variant. May it's too restrictive, though.
+  // TODO(vogelheim): "HTML parser allows only ascii" is no longer true.
   return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
          (ch >= '0' && ch <= '9') || ch == ':' || ch == '-' || ch == '_';
 }
@@ -113,22 +114,15 @@
   if (!IsValidName(name))
     return Invalid();
 
-  // Normalize element name, using the GetMixedCaseElementNames table.
-  String normalized = name.LowerASCII();
-  const auto& mixed_case_names = GetMixedCaseElementNames();
-  const auto iter = mixed_case_names.find(normalized);
-  if (iter != mixed_case_names.end())
-    normalized = iter->value;
-
   // Handle namespace prefixes:
-  wtf_size_t pos = normalized.find(':');
+  wtf_size_t pos = name.find(':');
   // Two (or more) colons => invalid.
-  if (pos != WTF::kNotFound && normalized.find(':', pos + 1) != WTF::kNotFound)
+  if (pos != WTF::kNotFound && name.find(':', pos + 1) != WTF::kNotFound)
     return Invalid();
   // No prefix, or the ones explicitly allowed by the spec: okay.
-  if (pos == WTF::kNotFound || normalized.StartsWith("svg:") ||
-      normalized.StartsWith("math:")) {
-    return normalized;
+  if (pos == WTF::kNotFound || name.StartsWith("svg:") ||
+      name.StartsWith("math:")) {
+    return name;
   }
   // All else: invalid.
   return Invalid();
@@ -138,19 +132,12 @@
   if (!IsValidName(name))
     return Invalid();
 
-  // Normalize attribute name, using the GetMixedCaseAttributeNames table.
-  String normalized = name.LowerASCII();
-  const auto& mixed_case_names = GetMixedCaseAttributeNames();
-  const auto iter = mixed_case_names.find(normalized);
-  if (iter != mixed_case_names.end())
-    normalized = iter->value;
-
   // The spec allows only a specific list of prefixed attributes. Use the
   // GetBaselineAllowAttributes() table to check for those. All other uses
   // of colon are invalid.
-  if (normalized.find(':') == WTF::kNotFound ||
-      GetBaselineAllowAttributes().Contains(normalized))
-    return normalized;
+  if (name.find(':') == WTF::kNotFound ||
+      GetBaselineAllowAttributes().Contains(name))
+    return name;
   return Invalid();
 }
 
diff --git a/third_party/blink/renderer/modules/sanitizer_api/element_sanitizer.idl b/third_party/blink/renderer/modules/sanitizer_api/element_sanitizer.idl
index 4648722..38c7a04 100644
--- a/third_party/blink/renderer/modules/sanitizer_api/element_sanitizer.idl
+++ b/third_party/blink/renderer/modules/sanitizer_api/element_sanitizer.idl
@@ -12,6 +12,7 @@
 
 [
   RuntimeEnabled=SanitizerAPIv0,
+  SecureContext,
   ImplementedAs=ElementSanitizer
 ] partial interface Element {
     [CEReactions, RaisesException, CallWith=ScriptState, MeasureAs=SanitizerAPIElementSetSanitized] void setHTML(DOMString markup, optional ElementSetHTMLOptions options = {});
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
index be1f956..d048130 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
@@ -41,6 +41,8 @@
 #include <memory>
 
 #include "base/numerics/checked_math.h"
+#include "media/base/video_color_space.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/third_party/skcms/skcms.h"
 
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
@@ -158,8 +160,73 @@
   buffer.SetRequiredPreviousFrameIndex(previous_frame_index);
 }
 
-inline std::unique_ptr<ColorProfile> ReadColorProfile(png_structp png,
-                                                      png_infop info) {
+// Returns nullptr if the cICP chunk is invalid, or if it describes an
+// unsupported color profile.
+// See https://w3c.github.io/PNG-spec/#11cICP for the definition of this chunk.
+static std::unique_ptr<ColorProfile> ParseCicpChunk(
+    const png_unknown_chunk& chunk) {
+  // First, validate the cICP chunk.
+  // cICP must be 4 bytes.
+  if (chunk.size != 4) {
+    return nullptr;
+  }
+
+  // Memory layout: ptmf, with p representing the colour primaries, t
+  // representing the transfer characteristics, m the matrix coefficients, and f
+  // whether the data is full or limited range.
+  uint8_t primaries = chunk.data[0];
+  uint8_t trc = chunk.data[1];
+  uint8_t matrix_coefficients = chunk.data[2];
+  uint8_t range_u8 = chunk.data[3];
+
+  // Per PNG spec, matrix_coefficients must be 0, i.e. RGB (YUV is explicitly
+  // disallowed).
+  if (matrix_coefficients) {
+    return nullptr;
+  }
+  // range must be 0 or 1.
+  if (range_u8 != 0 && range_u8 != 1) {
+    return nullptr;
+  }
+  const auto range = range_u8 == 1 ? gfx::ColorSpace::RangeID::FULL
+                                   : gfx::ColorSpace::RangeID::LIMITED;
+  if (range == gfx::ColorSpace::RangeID::LIMITED) {
+    // TODO(crbug/1339019): Implement this if needed.
+    DLOG(WARNING) << "Limited range RGB is not fully supported";
+  }
+  media::VideoColorSpace color_space(primaries, trc, 0, range);
+
+  // If not valid, do not return anything.
+  if (!color_space.IsSpecified()) {
+    return nullptr;
+  }
+
+  sk_sp<SkColorSpace> sk_color_space =
+      color_space.ToGfxColorSpace().GetAsFullRangeRGB().ToSkColorSpace();
+  skcms_ICCProfile profile;
+  sk_color_space->toProfile(&profile);
+
+  return std::make_unique<ColorProfile>(profile);
+}
+
+static inline std::unique_ptr<ColorProfile> ReadColorProfile(png_structp png,
+                                                             png_infop info) {
+  png_unknown_chunkp unknown_chunks;
+  size_t num_unknown_chunks =
+      png_get_unknown_chunks(png, info, &unknown_chunks);
+  for (size_t i = 0; i < num_unknown_chunks; i++) {
+    const auto& chunk = unknown_chunks[i];
+    if (strcmp(reinterpret_cast<const char*>(chunk.name), "cICP") == 0) {
+      // We found a cICP chunk, which takes priority over other chunks.
+      std::unique_ptr<ColorProfile> cicp_color_profile = ParseCicpChunk(chunk);
+      // Ignore cICP if it is invalid or if the color profile it describes is
+      // not supported.
+      if (cicp_color_profile) {
+        return cicp_color_profile;
+      }
+    }
+  }
+
   if (png_get_valid(png, info, PNG_INFO_sRGB)) {
     return std::make_unique<ColorProfile>(*skcms_sRGB_profile());
   }
@@ -651,7 +718,7 @@
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
           SetRGBAPremultiplyRowNeon(src_ptr, width, dst_row, &alpha_mask);
 #else
-          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+          for (auto* dst_pixel = dst_row; dst_pixel < dst_row + width;
                dst_pixel++, src_ptr += 4) {
             ImageFrame::SetRGBAPremultiply(dst_pixel, src_ptr[0], src_ptr[1],
                                            src_ptr[2], src_ptr[3]);
@@ -662,7 +729,7 @@
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
           SetRGBARawRowNeon(src_ptr, width, dst_row, &alpha_mask);
 #else
-          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+          for (auto* dst_pixel = dst_row; dst_pixel < dst_row + width;
                dst_pixel++, src_ptr += 4) {
             ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
                                    src_ptr[2], src_ptr[3]);
@@ -676,14 +743,14 @@
         // can blend the pixel of this frame, stored in |src_ptr|, over the
         // previous pixel stored in |dst_pixel|.
         if (buffer.PremultiplyAlpha()) {
-          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+          for (auto* dst_pixel = dst_row; dst_pixel < dst_row + width;
                dst_pixel++, src_ptr += 4) {
             ImageFrame::BlendRGBAPremultiplied(
                 dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]);
             alpha_mask &= src_ptr[3];
           }
         } else {
-          for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+          for (auto* dst_pixel = dst_row; dst_pixel < dst_row + width;
                dst_pixel++, src_ptr += 4) {
             ImageFrame::BlendRGBARaw(dst_pixel, src_ptr[0], src_ptr[1],
                                      src_ptr[2], src_ptr[3]);
@@ -699,7 +766,7 @@
 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
       SetRGBARawRowNoAlphaNeon(src_ptr, width, dst_row);
 #else
-      for (auto *dst_pixel = dst_row; dst_pixel < dst_row + width;
+      for (auto* dst_pixel = dst_row; dst_pixel < dst_row + width;
            src_ptr += 3, ++dst_pixel) {
         ImageFrame::SetRGBARaw(dst_pixel, src_ptr[0], src_ptr[1], src_ptr[2],
                                255);
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
index 94088c8f..bdef5f5 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder_test.cc
@@ -1456,6 +1456,23 @@
   ASSERT_FALSE(decoder->Failed());
 }
 
+TEST(PNGTests, cicp) {
+  const char* png_file = "/images/resources/cicp_pq.png";
+  scoped_refptr<SharedBuffer> data = ReadFile(png_file);
+  ASSERT_TRUE(data);
+
+  auto decoder = CreatePNGDecoder();
+  decoder->SetData(data.get(), true);
+  auto* frame = decoder->DecodeFrameBufferAtIndex(0);
+  ASSERT_TRUE(frame);
+  ASSERT_FALSE(decoder->Failed());
+  ASSERT_TRUE(decoder->HasEmbeddedColorProfile());
+  ColorProfileTransform* transform = decoder->ColorTransform();
+
+  const skcms_ICCProfile* png_profile = transform->SrcProfile();
+  EXPECT_TRUE(skcms_TransferFunction_isPQish(&png_profile->trc[0].parametric));
+}
+
 TEST(AnimatedPNGTests, TrnsMeansAlpha) {
   const char* png_file =
       "/images/resources/"
diff --git a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
index 9a352c3f0..ffd28fed 100644
--- a/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
+++ b/third_party/blink/renderer/platform/image-decoders/png/png_image_reader.cc
@@ -96,6 +96,11 @@
       ignore_animation_(false) {
   png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, pngFailed,
                                 nullptr);
+  // Configure the PNG encoder to always keep the cICP chunk if present.
+  // TODO(veluca): when libpng starts supporting cICP chunks explicitly, remove
+  // this code.
+  png_set_keep_unknown_chunks(png_, PNG_HANDLE_CHUNK_ALWAYS,
+                              reinterpret_cast<const png_byte*>("cICP"), 1);
   info_ = png_create_info_struct(png_);
   png_set_progressive_read_fn(png_, decoder_, nullptr, pngRowAvailable,
                               pngFrameComplete);
@@ -632,7 +637,8 @@
       ignore_animation_ = true;
     } else {
       auto is_necessary_ancillary = [](const png_byte* chunk) {
-        for (const char* tag : {"tRNS", "cHRM", "iCCP", "sRGB", "gAMA"}) {
+        for (const char* tag :
+             {"tRNS", "cHRM", "iCCP", "sRGB", "gAMA", "cICP"}) {
           if (IsChunk(chunk, tag))
             return true;
         }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 5340cb3..6c88d06 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3359,6 +3359,10 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Win11 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/debug-key-test.sub.https.html?include=source [ Timeout ]
+crbug.com/626703 [ Win11 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/simple-event-level-report-test.sub.https.html [ Timeout ]
+crbug.com/626703 [ Win11 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/source-priority-test.sub.https.html [ Timeout ]
+crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-interpolation-007.https.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/fetch/api/response/response-clone.any.serviceworker.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/html/browsers/browsing-the-web/back-forward-cache/eligibility/broadcast-channel.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/conversions-debug-mode/wpt_internal/attribution-reporting/navigation-source-trigger-data-cardinality-test.sub.https.html [ Timeout ]
@@ -6191,7 +6195,6 @@
 crbug.com/1249176 [ Mac11-arm64 ] webaudio/Oscillator/no-dezippering.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] virtual/offsetparent-old-behavior/external/wpt/shadow-dom/accesskey.tentative.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] external/wpt/html/semantics/embedded-content/media-elements/interfaces/TextTrackCue/endTime.html [ Failure ]
-crbug.com/1249176 [ Mac11-arm64 ] external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] external/wpt/shadow-dom/accesskey.tentative.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] external/wpt/service-workers/idlharness.https.any.worker.html [ Failure ]
 crbug.com/1249176 [ Mac11-arm64 ] external/wpt/dom/xslt/transformToFragment.tentative.window.html [ Failure ]
@@ -7044,3 +7047,7 @@
 
 # Flaky on multiple platforms
 crbug.com/1338536 http/tests/devtools/oopif/oopif-elements-nesting.js [ Failure Pass ]
+
+# Sheriff 2022-06-24
+crbug.com/1339211 [ Mac ] external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html [ Failure Pass ]
+crbug.com/1339214 [ Mac ] external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index e56f0c79..8e170e7 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -2926,7 +2926,6 @@
 crbug.com/1050754 external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unfocusable.html [ Failure ]
 crbug.com/1050754 external/wpt/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document.html [ Failure ]
 crbug.com/1050754 external/wpt/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html [ Timeout ]
-crbug.com/1050754 external/wpt/html/semantics/interfaces.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/semantics/links/downloading-resources/header-origin-no-referrer.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/semantics/links/following-hyperlinks/activation-behavior.window.html [ Failure Pass ]
 crbug.com/1050754 external/wpt/html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html [ Timeout ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index e2ff4ec..2d3bea9 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: cc4ca5bbe25751e48f46aeb4cc4232780732d3ea
+Version: 0a7622151c105cae2e4139031a26393da3a96b76
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 4fd376d..84e904c 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
@@ -1798,6 +1798,13 @@
         {}
        ]
       ],
+      "move-linebreak-to-different-column.html": [
+       "aff5ddce0cdec8bb40584afc04fc837466494610",
+       [
+        null,
+        {}
+       ]
+      ],
       "multicol-block-in-inline-crash.html": [
        "037b558ac0be08da6dfa6868b06e7a03e44031de",
        [
@@ -98386,6 +98393,19 @@
        ],
        {}
       ]
+     ],
+     "quote-scoping-empty-style-boundaries.html": [
+      "89614565ba9787f91dc5ae65d92cdcd8aea82e13",
+      [
+       null,
+       [
+        [
+         "/css/css-contain/reference/quote-scoping-empty-style-boundaries-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
      ]
     },
     "css-content": {
@@ -224177,7 +224197,7 @@
       ]
      ],
      "css-filters-animation-blur.html": [
-      "7a09e83a315f9649e3958f154bee7caff108fd14",
+      "f1bedd1c245dec34e9cc51f0138a32806a356f54",
       [
        null,
        [
@@ -224186,7 +224206,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            5
+           ],
+           [
+            0,
+            16000
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "css-filters-animation-brightness.html": [
@@ -224203,7 +224239,7 @@
       ]
      ],
      "css-filters-animation-combined-001.html": [
-      "78739036dfe7f43d7808ddd80e3033eca9109788",
+      "db917daa9a03c1ae757633fb8524e3234d052024",
       [
        null,
        [
@@ -224212,7 +224248,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            1
+           ],
+           [
+            0,
+            10000
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "css-filters-animation-contrast.html": [
@@ -264475,6 +264527,10 @@
        "2bc8d6730e756b6f1569d619b0accf5ce15d4e39",
        []
       ],
+      "quote-scoping-empty-style-boundaries-ref.html": [
+       "6437e08eb1a336d7ae9d69b1c1a47b67a3fd1a3f",
+       []
+      ],
       "ref-if-there-is-no-red.xht": [
        "a5b4e9f47a8e60ad0bede1ac81e02b3542c80f3b",
        []
@@ -297201,7 +297257,7 @@
       []
      ],
      "testharness.md": [
-      "2a13c4c7f1101d16adfa3aba8c20d1fd2c0c330c",
+      "fd4450f44033814f92bcb278f4a588823fe5060e",
       []
      ],
      "tools.md": [
@@ -326260,7 +326316,7 @@
      []
     ],
     "utils.sub.js": [
-     "705de30bbd6f806d85e9d8f3e95b55b994ffb23a",
+     "904f3d8d64812432e20dddcd2fbac176d05952ba",
      []
     ]
    },
@@ -327400,7 +327456,7 @@
        []
       ],
       "about-blank-replacement-blank-nested-frame.html": [
-       "99d07a48cda5ea82abbe2be777aedfcb4d1ddbde",
+       "16f7e7c60ffaf53f84a05de75c97ce20f7a6579f",
        []
       ],
       "about-blank-replacement-frame.py": [
@@ -333704,6 +333760,10 @@
      }
     }
    },
+   "webkit-mask-box-enumeration.html": [
+    "048170262a23b6929993518fca7a5a56d23614e0",
+    []
+   ],
    "webmessaging": {
     "DIR_METADATA": [
      "b431244896dbcdb96e0199478d7289603f79cfd1",
@@ -360119,7 +360179,7 @@
    },
    "compat": {
     "css-style-declaration-alias-enumeration.html": [
-     "75776e19c3b92fec92cae31d74850dcd6f67ee05",
+     "8244afd29dbc9bc7e6b2ab4390c09fbc7cab6c24",
      [
       null,
       {}
@@ -361653,6 +361713,13 @@
        null,
        {}
       ]
+     ],
+     "default-src-strict_dynamic_and_unsafe_inline.html": [
+      "bf45820ade2d75e4405ddc1780780345dcf7c997",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "embedded-enforcement": {
@@ -374060,6 +374127,13 @@
         {}
        ]
       ],
+      "font-relative-calc-dynamic.html": [
+       "54f01d45a396ca1320905e84abf70fac238b03ea",
+       [
+        null,
+        {}
+       ]
+      ],
       "font-relative-units-dynamic.html": [
        "63e17c9a540175edba5083f7ecab9ea9011738d5",
        [
@@ -386531,7 +386605,7 @@
         ]
        ],
        "shape-margin-005.html": [
-        "e19fcaf83cfe8fac41f50549e473e5fe2373b944",
+        "d39111115c4e05ab86f80cbd39c029f46274e882",
         [
          null,
          {}
@@ -444241,7 +444315,7 @@
          ]
         ],
         "createImageBitmap-sizeOverflow.html": [
-         "7239c2e7a1c5983583e04d8bce7ebe028401ffec",
+         "1be8184da60d63174972a82036d6b0d47d5f9a67",
          [
           null,
           {}
@@ -467979,7 +468053,7 @@
          ]
         ],
         "resource-selection-invoke-in-sync-event.html": [
-         "55fc7c20d3a0e0a8cd850df0b18d15a4c2b950be",
+         "1635598efdd28de1a9c40ab62d3a4f75b77d7ea4",
          [
           null,
           {}
@@ -468084,7 +468158,7 @@
          ]
         ],
         "resource-selection-invoke-remove-from-document.html": [
-         "23e7104cab8c6ac6a9447e4e654d2518e5b0ea70",
+         "65d0f73114999d7a1efef3d59b64bc94c9da9bd8",
          [
           null,
           {}
@@ -468847,7 +468921,7 @@
           ]
          ],
          "track-selection-metadata.html": [
-          "1f7df3b68232eef2010b7c8df665ec39b68af08e",
+          "c4d88a35f0d3ee34699c98d4eff51a3405c81d08",
           [
            null,
            {}
@@ -474319,7 +474393,7 @@
       }
      },
      "interfaces.html": [
-      "9cb7f5eaef1cc1903b0f13ebe28e0932973539af",
+      "dc7b600e0a0eebd699b51dbfc0bb519e4f375846",
       [
        null,
        {}
@@ -474539,7 +474613,7 @@
        ]
       ],
       "popup-animated-hide-cleanup.tentative.html": [
-       "762458ea669dda88b9f725c7d4dabf8179cf2b44",
+       "99fc2b895761d06f1e36a5ccaab096d9746f644d",
        [
         null,
         {
@@ -474548,14 +474622,14 @@
        ]
       ],
       "popup-animation-corner-cases.tentative.html": [
-       "2edcca5b51adc381f02744c278004684826a96a4",
+       "96812870268120b8bd4390908c98d606f0b0216c",
        [
         null,
         {}
        ]
       ],
       "popup-attribute-basic.tentative.html": [
-       "22742a3a6edcd143184fbd29a6a2ca211e7f8a44",
+       "5e26a725761f0dc725d5e89bf703f5074c47b706",
        [
         null,
         {}
@@ -474590,7 +474664,7 @@
        ]
       ],
       "popup-focus.tentative.html": [
-       "85f328f0e10345bb03e29ccf3f210226e47380ec",
+       "f9868369a2e1cbafafc5b2daeec82563584d2af0",
        [
         null,
         {
@@ -474600,7 +474674,7 @@
        ]
       ],
       "popup-invoking-attribute.tentative.html": [
-       "4a40768647156a678d18199f239164f3eddfe33a",
+       "eba8f553f60925e89bae5314b16b58bd6cf8ff86",
        [
         null,
         {
@@ -474664,7 +474738,7 @@
        ]
       ],
       "popup-top-layer-interactions.tentative.html": [
-       "c8f9ddf658f8f5168cac30398d9b10a5d1a16aa3",
+       "9a39d9f2d9fdf5d0ce9be47708357e2607a84a46",
        [
         null,
         {
@@ -475510,13 +475584,6 @@
           {}
          ]
         ],
-        "080.html": [
-         "bd983355a1f84f076283968393391ff53815cdfe",
-         [
-          null,
-          {}
-         ]
-        ],
         "081.html": [
          "1b9bc991c69355c499e801f9cc2bc56dfda7da7d",
          [
diff --git a/third_party/blink/web_tests/external/wpt/compat/css-style-declaration-alias-enumeration.html b/third_party/blink/web_tests/external/wpt/compat/css-style-declaration-alias-enumeration.html
index 75776e19..8244afd 100644
--- a/third_party/blink/web_tests/external/wpt/compat/css-style-declaration-alias-enumeration.html
+++ b/third_party/blink/web_tests/external/wpt/compat/css-style-declaration-alias-enumeration.html
@@ -10,25 +10,28 @@
 const PREFIXED_PROPS = [
   'webkitAlignContent',
   'webkitAlignItems',
-  'webkitAnimationName',
   'webkitAlignSelf',
-  'webkitAnimationDuration',
-  'webkitAnimationTimingFunction',
-  'webkitAnimationIterationCount',
-  'webkitAnimationDirection',
-  'webkitAnimationPlayState',
-  'webkitAnimationDelay',
-  'webkitAnimationFillMode',
   'webkitAnimation',
+  'webkitAnimationDelay',
+  'webkitAnimationDirection',
+  'webkitAnimationDuration',
+  'webkitAnimationFillMode',
+  'webkitAnimationIterationCount',
+  'webkitAnimationName',
+  'webkitAnimationPlayState',
+  'webkitAnimationTimingFunction',
   'webkitBackfaceVisibility',
+  'WebKitBackgroundClip',
   'webkitBackgroundOrigin',
   'webkitBackgroundSize',
   'webkitBorderBottomLeftRadius',
   'webkitBorderBottomRightRadius',
+  'webkitBorderRadius',
   'webkitBorderTopLeftRadius',
   'webkitBorderTopRightRadius',
-  'webkitBorderRadius',
   'webkitBoxShadow',
+  'webkitBoxSizing',
+  'webkitFilter',
   'webkitFlex',
   'webkitFlexBasis',
   'webkitFlexDirection',
@@ -36,15 +39,8 @@
   'webkitFlexGrow',
   'webkitFlexShrink',
   'webkitFlexWrap',
-  'webkitFilter',
   'webkitJustifyContent',
   'webkitMask',
-  'webkitMaskBoxImage',
-  'webkitMaskBoxImageOutset',
-  'webkitMaskBoxImageRepeat',
-  'webkitMaskBoxImageSlice',
-  'webkitMaskBoxImageSource',
-  'webkitMaskBoxImageWidth',
   'webkitMaskClip',
   'webkitMaskComposite',
   'webkitMaskImage',
@@ -55,14 +51,14 @@
   'webkitOrder',
   'webkitPerspective',
   'webkitPerspectiveOrigin',
+  'webkitTransform',
   'webkitTransformOrigin',
   'webkitTransformStyle',
-  'webkitTransform',
+  'webkitTransition',
   'webkitTransitionDelay',
   'webkitTransitionDuration',
   'webkitTransitionProperty',
   'webkitTransitionTimingFunction',
-  'webkitTransition',
 ];
 
 const docBodyStyle = document.body.style;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
index 327c3d020..5b2ed7f3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/at-container-parsing.html
@@ -172,4 +172,16 @@
   test_container_selector_invalid('And');
   test_container_selector_invalid('oR');
   test_container_selector_invalid('nOt');
+
+  test_query_known('style(--my-prop: foo)');
+  test_query_known('style(--my-prop: foo - bar ())');
+  test_query_known('style(not ((--foo: calc(10px + 2em)) and ((--foo: url(x)))))');
+  test_query_known('style((--foo: bar) or (--bar: 10px))');
+  test_query_known('style(--foo: bar !important)');
+  test_query_known('style(--my-prop:)');
+  test_query_known('style(--my-prop: )');
+
+  test_query_unknown('style(--foo: bar;)');
+  test_query_unknown('style(--foo)');
+  test_query_unknown('style(style(--foo: bar))');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/quote-scoping-empty-style-boundaries.html b/third_party/blink/web_tests/external/wpt/css/css-contain/quote-scoping-empty-style-boundaries.html
new file mode 100644
index 0000000..8961456
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/quote-scoping-empty-style-boundaries.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-contain test: nested style containment and the quote element following a style boundary without any quotes</title>
+  <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
+  <link rel="match" href="reference/quote-scoping-empty-style-boundaries-ref.html">
+  <link rel=help href="https://drafts.csswg.org/css-contain-1/#containment-style">
+
+<div style="contain: style;">
+    <div style="contain: style;">
+        <q></q>
+    </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/reference/quote-scoping-empty-style-boundaries-ref.html b/third_party/blink/web_tests/external/wpt/css/css-contain/reference/quote-scoping-empty-style-boundaries-ref.html
new file mode 100644
index 0000000..6437e08e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/reference/quote-scoping-empty-style-boundaries-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang=en>
+  <meta charset=utf-8>
+  <title>CSS-contain test: nested style containment and the quote element following a style boundary without any quotes</title>
+  <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
+  <link rel=help href="https://drafts.csswg.org/css-contain-1/#containment-style">
+
+<div>
+    <div>
+        <q></q>
+    </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testharness.md b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testharness.md
index 2a13c4c7..fd4450f 100644
--- a/third_party/blink/web_tests/external/wpt/docs/writing-tests/testharness.md
+++ b/third_party/blink/web_tests/external/wpt/docs/writing-tests/testharness.md
@@ -241,7 +241,7 @@
 <meta name="variant" content="?2001-last">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/common/subset-tests.js">
+<script src="/common/subset-tests.js"></script>
 <script>
  const tests = [
                  { fn: t => { ... }, name: "..." },
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interfaces.html b/third_party/blink/web_tests/external/wpt/html/semantics/interfaces.html
index 9cb7f5e..dc7b600 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interfaces.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interfaces.html
@@ -17,6 +17,8 @@
     if (variant === "useNS") {
       // Use createElementNS here to preserve the case of local_name.
       e = document.createElementNS("http://www.w3.org/1999/xhtml", local_name);
+    } else if (variant === "useParser") {
+      e = new DOMParser().parseFromString("<" + local_name + ">", "text/html").querySelector(local_name);
     } else {
       e = document.createElement(local_name);
     }
@@ -31,17 +33,42 @@
                 "Element " + local_name + " should implement Element.");
     assert_true(e instanceof Node,
                 "Element " + local_name + " should implement Node.");
-  }, "Interfaces for " + local_name);
+  }, "Interfaces for " + local_name + ": " + variant);
+}
+
+// Some elements have weird parser behavior / insertion modes and would be
+// skipped by the parser, so skip those.
+function should_do_parser_test(local_name) {
+  return ![
+    "foo-BAR",
+    "tbody",
+    "td",
+    "tfoot",
+    "th",
+    "thead",
+    "tr",
+    "å-bar",
+    "caption",
+    "col",
+    "colgroup",
+    "frame",
+    "image",
+    "frameset",
+  ].includes(local_name)
 }
 
 elements.forEach(function(a) {
   do_test(a[0], a[1], "useNS");
 
+  if (should_do_parser_test(a[0])) {
+    do_test(a[0], a[1], "useParser");
+  }
+
   // Only run the createElement variant if the input is all-lowercase, because createElement
   // case-folds to lowercase. Custom elements are required to use all-lowercase to implement
   // HTMLElement, otherwise they use HTMLUnknownElement per spec. Example: "foo-BAR".
   if (a[0] === a[0].toLowerCase()) {
-    do_test(a[0].toUpperCase(), a[1]);
+    do_test(a[0].toUpperCase(), a[1], "createElement");
   }
 })
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-insecure-context.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-insecure-context.html
new file mode 100644
index 0000000..4b185fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-insecure-context.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+  // Currently, the Sanitizer requires a secure context.
+  test(t => {
+    assert_false(globalThis.isSecureContext);
+    assert_equals("Sanitizer" in globalThis, globalThis.isSecureContext);
+    assert_equals("setHTML" in document.body, globalThis.isSecureContext);
+  }, "Sanitizer API in an insecure context.");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-names.https.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-names.https.html
index 22b9135..635b797 100644
--- a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-names.https.html
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-names.https.html
@@ -103,15 +103,12 @@
       assert_equals(sanitize(elem, probe), probe);
     }, `Mixed-case element names #${index}: "${elem}"`);
     test(t => {
-      assert_equals(sanitize(elem.toLowerCase(), probe), probe);
+      assert_not_equals(sanitize(elem.toLowerCase(), probe), probe);
     }, `Mixed-case element names #${index}: "${elem.toLowerCase()}"`);
     test(t => {
-      assert_equals(sanitize(elem.toUpperCase(), probe), probe);
+      assert_not_equals(sanitize(elem.toUpperCase(), probe), probe);
     }, `Mixed-case element names #${index}: "${elem.toUpperCase()}"`);
     test(t => {
-      assert_equals(sanitize(elem, probe.toLowerCase()), probe);
-    }, `Mixed-case element names #${index}: "${elem}" with lowercase probe.`);
-    test(t => {
       const elems = [elem];
       assert_array_equals(
         new Sanitizer({allowElements: elems}).getConfiguration().allowElements,
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-query-config.https.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-query-config.https.html
index bd7c7ea3..8824ba7 100644
--- a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-query-config.https.html
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-query-config.https.html
@@ -65,7 +65,7 @@
                          "onclick": ["*"] },
       allowCustomElements: true,
     };
-    assert_deep_equals(configs[0],
+    assert_deep_equals(config_0_mixed,
                        new Sanitizer(config_0_mixed).getConfiguration());
   }, "SanitizerAPI getConfiguration() reflects creation config.");
 
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-secure-context.https.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-secure-context.https.html
new file mode 100644
index 0000000..0e04e04d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-secure-context.https.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+  // Currently, the Sanitizer requires a secure context.
+  test(t => {
+    assert_true(globalThis.isSecureContext);
+    assert_equals("Sanitizer" in globalThis, globalThis.isSecureContext);
+    assert_equals("setHTML" in document.body, globalThis.isSecureContext);
+  }, "SanitizerAPI in a secure context.");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/support/testcases.sub.js b/third_party/blink/web_tests/external/wpt/sanitizer-api/support/testcases.sub.js
index e0efa1c..c54d4461 100644
--- a/third_party/blink/web_tests/external/wpt/sanitizer-api/support/testcases.sub.js
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/support/testcases.sub.js
@@ -26,7 +26,6 @@
   {config_input: {dropElements: ["custom-element"], allowCustomElements: true}, value: "<custom-element>test</custom-element>bla", result: "bla", message: "allow custom elements with drop list contains [\"custom-element\"]"},
   {config_input: {dropElements: ["script"]}, value: "<script>alert('i am a test')<\/script>", result: "", message: "test script with [\"script\"] as dropElements list"},
   {config_input: {dropElements: ["test-element", "i"]}, value: "<div>balabala<i>test</i></div><test-element>t</test-element>", result: "<div>balabala</div>", message: "dropElements list [\"test-element\", \"i\"]}"},
-  {config_input: {dropElements: ["I", "DL"]}, value: "<div>balabala<dl>test</dl></div>", result: "<div>balabala</div>", message: "dropElements list [\"I\", \"DL\"]}"},
   {config_input: {dropElements: ["dl", "p"]}, value: "<div>balabala<i>i</i><p>t</p></div>", result: "<div>balabala<i>i</i></div>", message: "dropElements list [\"dl\", \"p\"]}"},
   {config_input: {dropElements: [123, [], "test", "i", "custom-element"]}, value: "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>", result: "<div>balabala</div>", message: "dropElements list with invalid values"},
   {config_input: {blockElements: [123, [], "test", "i", "custom-element"]}, value: "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>", result: "<div>balabalatest</div>t", message: "blockElements list with invalid values"},
@@ -37,7 +36,6 @@
   {config_input: {dropAttributes: {"*": ["a"]}}, value: "<a id='a' style='color: black'>Click.</a><div style='color: white'>div</div>", result: "<a>Click.</a><div style=\"color: white\">div</div>", message: "dropAttributes list {\"*\": [\"a\"]} with style attribute"},
   {config_input: {dropAttributes: {}}, value: "<p id='test'>Click.</p>", result: "<p id=\"test\">Click.</p>", message: "empty dropAttributes list with id attribute"},
   {config_input: {dropAttributes: {"id": ["*"]}}, value: "<p id='test'>Click.</p>", result: "<p>Click.</p>", message: "dropAttributes list {\"id\": [\"*\"]} with id attribute"},
-  {config_input: {dropAttributes: {"ID": ["*"]}}, value: "<p id='test'>Click.</p>", result: "<p>Click.</p>", message: "dropAttributes list {\"ID\": [\"*\"]} with id attribute"},
   {config_input: {dropAttributes: {"data-attribute-with-dashes": ["*"]}}, value: "<p id='p' data-attribute-with-dashes='123'>Click.</p><script>document.getElementById('p').dataset.attributeWithDashes=123;</script>", result: "<p id=\"p\">Click.</p>", message: "dropAttributes list {\"data-attribute-with-dashes\": [\"*\"]} with dom dataset js access"},
   {config_input: {allowAttributes: {"id": ["div"]}}, value: "<p id='p'>P</p><div id='div'>DIV</div>", result: "<p>P</p><div id=\"div\">DIV</div>", message: "allowAttributes list {\"id\": [\"div\"]} with id attribute"},
   {config_input: {allowAttributes: {"id": ["*"]}}, value: "<p id='test' onclick='a= 123'>Click.</p>", result: "<p id=\"test\">Click.</p>", message: "allowAttributes list {\"id\": [\"*\"]} with id attribute and onclick scripts"},
@@ -68,4 +66,12 @@
   {config_input: {allowComments: false}, value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>", result: "<p>commentintext</p>", message: "HTML with comments deeper in the tree, !allowComments"},
   {config_input: {allowElements: ["svg"]}, value: "<svg></svg>", result: "", message: "Unknown HTML names (HTMLUnknownElement instances) should not match elements parsed as non-HTML namespaces."},
   {config_input: {allowElements: ["div", "svg"]}, value: "<div><svg></svg></div>", result: "<div></div>", message: "Unknown HTML names (HTMLUnknownElement instances) should not match elements parsed as non-HTML namespaces when nested."},
+
+  // Case normalization (actually: lack of)
+  {config_input: {dropElements: ["I", "DL"]}, value: "<div>balabala<dl>test</dl></div>", result: "<div>balabala<dl>test</dl></div>", message: "dropElements list [\"I\", \"DL\"]}"},
+  {config_input: {dropElements: ["i", "dl"]}, value: "<div>balabala<dl>test</dl></div>", result: "<div>balabala</div>", message: "dropElements list [\"i\", \"dl\"]}"},
+  {config_input: {dropElements: ["i", "dl"]}, value: "<DIV>balabala<DL>test</DL></DIV>", result: "<div>balabala</div>", message: "dropElements list [\"i\", \"dl\"]} with uppercase HTML"},
+  {config_input: {dropAttributes: {"ID": ["*"]}}, value: "<p id=\"test\">Click.</p>", result: "<p id=\"test\">Click.</p>", message: "dropAttributes list {\"ID\": [\"*\"]} with id attribute"},
+  {config_input: {dropAttributes: {"ID": ["*"]}}, value: "<p ID=\"test\">Click.</p>", result: "<p id=\"test\">Click.</p>", message: "dropAttributes list {\"ID\": [\"*\"]} with ID attribute"},
+  {config_input: {dropAttributes: {"id": ["*"]}}, value: "<p ID=\"test\">Click.</p>", result: "<p>Click.</p>", message: "dropAttributes list {\"id\": [\"*\"]} with ID attribute"},
 ];
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/utils.sub.js b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/utils.sub.js
index 705de30bb..904f3d8d 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/utils.sub.js
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/utils.sub.js
@@ -64,7 +64,21 @@
   frame.src = 'https://{{hosts[alt][]}}:{{ports[https][0]}}' +
       '/secure-payment-confirmation/resources/iframe-enroll.html';
 
-  return new Promise(resolve => {
+  // Wait for the iframe to load.
+  const readyPromise = new Promise(resolve => {
+      window.addEventListener('message', function handler(evt) {
+        if (evt.source === frame.contentWindow && evt.data.type == 'loaded') {
+          window.removeEventListener('message', handler);
+
+          resolve(evt.data);
+        }
+      });
+  });
+  document.body.appendChild(frame);
+  await readyPromise;
+
+  // Setup the result promise, and then trigger credential creation.
+  const resultPromise = new Promise(resolve => {
       window.addEventListener('message', function handler(evt) {
         if (evt.source === frame.contentWindow) {
           document.body.removeChild(frame);
@@ -73,8 +87,9 @@
           resolve(evt.data);
         }
       });
-      document.body.appendChild(frame);
   });
+  frame.contentWindow.postMessage({ userActivation: true }, '*');
+  return resultPromise;
 }
 
 function arrayBufferToString(buffer) {
diff --git a/third_party/blink/web_tests/external/wpt/webkit-mask-box-enumeration.html b/third_party/blink/web_tests/external/wpt/webkit-mask-box-enumeration.html
new file mode 100644
index 0000000..0481702
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webkit-mask-box-enumeration.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>WebKitMaxBoxImage on CSSStyleDeclaration</title>
+<link rel="help" href="https://compat.spec.whatwg.org/#css-simple-aliases">
+<meta name="assert" content="This test verifies that these WebKit properties are enumerated on CSSStyleDeclaration" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+</body>
+<script>
+const PREFIXED_PROPS = [
+  'webkitMaskBoxImage',
+  'webkitMaskBoxImageOutset',
+  'webkitMaskBoxImageRepeat',
+  'webkitMaskBoxImageSlice',
+  'webkitMaskBoxImageSource',
+  'webkitMaskBoxImageWidth',
+];
+
+const docBodyStyle = document.body.style;
+for (let prop of PREFIXED_PROPS) {
+  test(() => {
+    assert_true(prop in docBodyStyle);
+  }, `${prop} found on CSSStyleDeclaration`);
+}
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html b/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html
new file mode 100644
index 0000000..7f0d3cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/protocol/h264-profile-levels.https.html
@@ -0,0 +1,109 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection H.264 profile levels</title>
+<meta name="timeout" content="long">
+<script src="../third_party/sdp/sdp.js"></script>
+<script src="simulcast.js"></script>
+<script src="../RTCPeerConnection-helper.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+function mungeLevel(sdp, level) {
+  level_hex = Math.round(level * 10).toString(16);
+  return {
+    type: sdp.type,
+    sdp: sdp.sdp.replace(/(profile-level-id=....)(..)/g,
+                         "$1" + level_hex)
+  }
+}
+
+// Numbers taken from
+// https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels
+let levelTable = {
+  1: {mbs: 1485, fs: 99},
+  1.1: {mbs: 3000, fs: 396},
+  1.2: {mbs: 6000, fs: 396},
+  1.3: {mbs: 11880, fs: 396},
+  2: {mbs: 11880, fs: 396},
+  2.1: {mbs: 19800, fs: 792},
+  2.2: {mbs: 20250, fs: 1620},
+  3: {mbs: 40500, fs: 1620},
+  3.1: {mbs: 108000, fs: 3600},
+  3.2: {mbs: 216000, fs: 5120},
+  4: {mbs: 245760, fs: 8192},
+  4.1: {mbs: 245760, fs: 8192},
+  4.2: {mbs: 522240, fs: 8704},
+  5: {mbs: 589824, fs: 22800},
+  5.1: {mbs: 983040, fs: 36864},
+  5.2: {mbs: 2073600, fs: 36864},
+  6: {mbs: 4177920, fs: 139264},
+  6.1: {mbs: 8355840, fs: 139264},
+  6.2: {mbs: 16711680, fs: 139264},
+};
+
+function sizeFitsLevel(width, height, fps, level) {
+  const frameSizeMacroblocks = width * height / 256;
+  const macroblocksPerSecond = frameSizeMacroblocks * fps;
+  assert_less_than_equal(frameSizeMacroblocks,
+                         levelTable[level].fs, 'frame size');
+  assert_less_than_equal(macroblocksPerSecond,
+                         levelTable[level].mbs, 'macroblocks/second');
+}
+
+// Constant for now, may be variable later.
+const framesPerSecond = 30;
+
+for (let level of Object.keys(levelTable)) {
+  promise_test(async t => {
+    assert_implements('getCapabilities' in RTCRtpSender, 'RTCRtpSender.getCapabilities not supported');
+    assert_implements(RTCRtpSender.getCapabilities('video').codecs.find(c => c.mimeType === 'video/H264'), 'H264 not supported');
+
+    const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+    const pc2 = new RTCPeerConnection();
+    t.add_cleanup(() => pc2.close());
+    const v = document.createElement('video');
+
+    // Generate the largest video we can get from the attached device.
+    // This means platform inconsistency.
+    // The fake video in Chrome WPT tests is 3840x2160.
+    const stream = await navigator.mediaDevices.getUserMedia(
+      {video: {width: 12800, height: 7200, frameRate: framesPerSecond}});
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    const transceiver = pc1.addTransceiver(stream.getVideoTracks()[0], {
+      streams: [stream],
+    });
+    preferCodec(transceiver, 'video/H264');
+
+    let metadataLoaded = new Promise((resolve) => {
+      v.autoplay = true;
+      v.srcObject = stream;
+      v.id = stream.id
+      v.addEventListener('loadedmetadata', () => {
+        resolve();
+      });
+    });
+
+    const offer = await pc1.createOffer();
+    await pc1.setLocalDescription(offer),
+    await pc2.setRemoteDescription(offer);
+    const answer = await pc2.createAnswer();
+    await pc2.setLocalDescription(answer);
+    await pc1.setRemoteDescription(mungeLevel(answer, level));
+    await metadataLoaded;
+    // Ensure that H.264 is in fact used.
+    const statsReport = await transceiver.sender.getStats();
+    for (const stats of statsReport.values()) {
+      if (stats.type === 'outbound-rtp') {
+        const activeCodec = stats.codecId;
+        const codecStats = statsReport.get(activeCodec);
+        assert_implements_optional(codecStats.mimeType ==='video/H264',
+                          'Level ' + level + ' H264 video is not supported');
+      }
+    }
+    sizeFitsLevel(v.videoWidth, v.videoHeight, framesPerSecond, level);
+  }, 'Level ' + level + ' H264 video is appropriately constrained');
+
+}
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-navigation-context-downgrade.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-navigation-context-downgrade.js
index 6655c11..6d1eb70 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-navigation-context-downgrade.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/same-site-issue-warn-cookie-navigation-context-downgrade.js
@@ -10,17 +10,15 @@
   const setCookieUrl = 'http://cookie.test:8000/inspector-protocol/network/resources/set-cookie.php?cookie='
       + encodeURIComponent('name=value; SameSite=Strict');
   const baseURL = 'inspector-protocol/network/resources/hello-world.html'
-  const insecureUrl = 'http://cookie.test:8000/' + baseURL;
   const secureUrl = 'https://cookie.test:8443/' + baseURL;
 
-  // Set a SameSite=Strict cookie on the cookie.test domain
+  // Set a SameSite=Strict cookie on the cookie.test domain via an insecure URL.
+  let issuePromise = dp.Audits.onceIssueAdded();
   await session.navigate(setCookieUrl);
+  // Ignore the quirks mode issue
+  await issuePromise;
 
-  // Navigate first to an insecure site. Note: This isn't strictly necessary
-  // because the setCookieUrl is also insecure but this helps to illustrate the
-  // point as insecureUrl and secureUrl are the same URL except for the scheme.
-  await helper.navigateWithExtraInfo(insecureUrl);
-  const issuePromise = dp.Audits.onceIssueAdded();
+  issuePromise = dp.Audits.onceIssueAdded();
   // Now navigate to the secure site, this should trigger the issue.
   await helper.jsNavigateWithExtraInfo(secureUrl);
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/quota-opaque-origin.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/quota-opaque-origin.js
new file mode 100644
index 0000000..df1d23a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/quota-opaque-origin.js
@@ -0,0 +1,8 @@
+(async function (testRunner) {
+  const { page, session, dp } = await testRunner.startBlank(
+    `Tests that reading quota an opaque origin throws an error\n`);
+  await page.loadHTML("<iframe src='about:blank' sandbox></iframe>");
+  const response = await dp.Storage.getUsageAndQuota({ origin: 'about:blank' });
+  testRunner.log('Throws an expected error: ' + response.error.message);
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/images/resources/cicp_pq.png b/third_party/blink/web_tests/images/resources/cicp_pq.png
new file mode 100644
index 0000000..e2a1e57
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/cicp_pq.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/compat/css-style-declaration-alias-enumeration-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/compat/css-style-declaration-alias-enumeration-expected.txt
new file mode 100644
index 0000000..48d3608
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/compat/css-style-declaration-alias-enumeration-expected.txt
@@ -0,0 +1,55 @@
+This is a testharness.js-based test.
+Found 51 tests; 50 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS webkitAlignContent found on CSSStyleDeclaration
+PASS webkitAlignItems found on CSSStyleDeclaration
+PASS webkitAlignSelf found on CSSStyleDeclaration
+PASS webkitAnimation found on CSSStyleDeclaration
+PASS webkitAnimationDelay found on CSSStyleDeclaration
+PASS webkitAnimationDirection found on CSSStyleDeclaration
+PASS webkitAnimationDuration found on CSSStyleDeclaration
+PASS webkitAnimationFillMode found on CSSStyleDeclaration
+PASS webkitAnimationIterationCount found on CSSStyleDeclaration
+PASS webkitAnimationName found on CSSStyleDeclaration
+PASS webkitAnimationPlayState found on CSSStyleDeclaration
+PASS webkitAnimationTimingFunction found on CSSStyleDeclaration
+PASS webkitBackfaceVisibility found on CSSStyleDeclaration
+FAIL WebKitBackgroundClip found on CSSStyleDeclaration assert_true: expected true got false
+PASS webkitBackgroundOrigin found on CSSStyleDeclaration
+PASS webkitBackgroundSize found on CSSStyleDeclaration
+PASS webkitBorderBottomLeftRadius found on CSSStyleDeclaration
+PASS webkitBorderBottomRightRadius found on CSSStyleDeclaration
+PASS webkitBorderRadius found on CSSStyleDeclaration
+PASS webkitBorderTopLeftRadius found on CSSStyleDeclaration
+PASS webkitBorderTopRightRadius found on CSSStyleDeclaration
+PASS webkitBoxShadow found on CSSStyleDeclaration
+PASS webkitBoxSizing found on CSSStyleDeclaration
+PASS webkitFilter found on CSSStyleDeclaration
+PASS webkitFlex found on CSSStyleDeclaration
+PASS webkitFlexBasis found on CSSStyleDeclaration
+PASS webkitFlexDirection found on CSSStyleDeclaration
+PASS webkitFlexFlow found on CSSStyleDeclaration
+PASS webkitFlexGrow found on CSSStyleDeclaration
+PASS webkitFlexShrink found on CSSStyleDeclaration
+PASS webkitFlexWrap found on CSSStyleDeclaration
+PASS webkitJustifyContent found on CSSStyleDeclaration
+PASS webkitMask found on CSSStyleDeclaration
+PASS webkitMaskClip found on CSSStyleDeclaration
+PASS webkitMaskComposite found on CSSStyleDeclaration
+PASS webkitMaskImage found on CSSStyleDeclaration
+PASS webkitMaskOrigin found on CSSStyleDeclaration
+PASS webkitMaskPosition found on CSSStyleDeclaration
+PASS webkitMaskRepeat found on CSSStyleDeclaration
+PASS webkitMaskSize found on CSSStyleDeclaration
+PASS webkitOrder found on CSSStyleDeclaration
+PASS webkitPerspective found on CSSStyleDeclaration
+PASS webkitPerspectiveOrigin found on CSSStyleDeclaration
+PASS webkitTransform found on CSSStyleDeclaration
+PASS webkitTransformOrigin found on CSSStyleDeclaration
+PASS webkitTransformStyle found on CSSStyleDeclaration
+PASS webkitTransition found on CSSStyleDeclaration
+PASS webkitTransitionDelay found on CSSStyleDeclaration
+PASS webkitTransitionDuration found on CSSStyleDeclaration
+PASS webkitTransitionProperty found on CSSStyleDeclaration
+PASS webkitTransitionTimingFunction found on CSSStyleDeclaration
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt
new file mode 100644
index 0000000..5a65678
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/at-container-parsing-expected.txt
@@ -0,0 +1,111 @@
+This is a testharness.js-based test.
+Found 107 tests; 106 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS (width)
+PASS (min-width: 0px)
+PASS (max-width: 0px)
+PASS (height)
+PASS (min-height: 0px)
+PASS (max-height: 0px)
+PASS (aspect-ratio)
+PASS (min-aspect-ratio: 1/2)
+PASS (max-aspect-ratio: 1/2)
+PASS (orientation: portrait)
+PASS (inline-size)
+PASS (min-inline-size: 0px)
+PASS (max-inline-size: 0px)
+PASS (block-size)
+PASS (min-block-size: 0px)
+PASS (max-block-size: 0px)
+PASS (width: 100px)
+PASS ((width: 100px))
+PASS (not (width: 100px))
+PASS ((width: 100px) and (height: 100px))
+PASS (((width: 40px) or (width: 50px)) and (height: 100px))
+PASS ((width: 100px) and ((height: 40px) or (height: 50px)))
+PASS (((width: 40x) and (height: 50px)) or (height: 100px))
+PASS ((width: 50px) or ((width: 40px) and (height: 50px)))
+PASS ((width: 100px) and (not (height: 100px)))
+PASS (width < 100px)
+PASS (width <= 100px)
+PASS (width = 100px)
+PASS (width > 100px)
+PASS (width >= 100px)
+PASS (100px < width)
+PASS (100px <= width)
+PASS (100px = width)
+PASS (100px > width)
+PASS (100px >= width)
+PASS (100px < width < 200px)
+PASS (100px < width <= 200px)
+PASS (100px <= width < 200px)
+PASS (100px > width > 200px)
+PASS (100px > width >= 200px)
+PASS (100px >= width > 200px)
+PASS (width: calc(10px))
+PASS (width: calc(10em))
+PASS (width: calc(10px + 10em))
+PASS (width < calc(10px + 10em))
+PASS (width < max(10px, 10em))
+PASS (calc(10px + 10em) < width)
+PASS (calc(10px + 10em) < width < max(30px, 30em))
+PASS foo(width)
+PASS size(width)
+PASS (asdf)
+PASS (resolution > 100dpi)
+PASS (resolution: 150dpi)
+PASS (color)
+PASS (min-color: 1)
+PASS (color-index >= 1)
+PASS size(grid)
+PASS (grid)
+PASS (width == 100px)
+PASS (100px == width)
+PASS (100px = width = 200px)
+PASS (100px < width > 200px)
+PASS (100px <= width >= 200px)
+PASS (100px <= width > 200px)
+PASS (100px < width >= 200px)
+PASS (100px : width : 200px)
+PASS screen
+PASS print
+PASS not print
+PASS only print
+PASS screen and (width: 100px)
+PASS screen or (width: 100px)
+PASS not screen and (width: 100px)
+PASS not screen or (width: 100px)
+PASS (width: 100px), (height: 100px)
+PASS (width: 100px) and (height: 100px)
+PASS (width: 100px) or (height: 100px)
+PASS not (width: 100px)
+PASS foo (width: 100px)
+PASS Container selector: foo
+PASS Container selector:  foo
+PASS Container selector:  foo 
+PASS Container selector: foo foo
+PASS Container selector: 1px
+PASS Container selector: 50gil
+PASS Container selector: name(foo)
+PASS Container selector: type(inline-size)
+PASS Container selector: "foo"
+PASS Container selector: "inherit"
+PASS Container selector: none
+PASS Container selector: None
+PASS Container selector: and
+PASS Container selector: or
+PASS Container selector: not
+PASS Container selector: And
+PASS Container selector: oR
+PASS Container selector: nOt
+PASS style(--my-prop: foo)
+PASS style(--my-prop: foo - bar ())
+PASS style(not ((--foo: calc(10px + 2em)) and ((--foo: url(x)))))
+PASS style((--foo: bar) or (--bar: 10px))
+FAIL style(--foo: bar !important) assert_equals: expected "true" but got ""
+PASS style(--my-prop:)
+PASS style(--my-prop: )
+PASS style(--foo: bar;)
+PASS style(--foo)
+PASS style(style(--foo: bar))
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
new file mode 100644
index 0000000..0a5fb59
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+NOTRUN Level 1 H264 video is appropriately constrained Level 1 H264 video is not supported
+FAIL Level 2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 396 but got 32400
+FAIL Level 3 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 32400
+FAIL Level 4 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8192 but got 32400
+FAIL Level 5 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 22800 but got 32400
+NOTRUN Level 6 H264 video is appropriately constrained Level 6 H264 video is not supported
+NOTRUN Level 1.1 H264 video is appropriately constrained Level 1.1 H264 video is not supported
+NOTRUN Level 1.2 H264 video is appropriately constrained Level 1.2 H264 video is not supported
+NOTRUN Level 1.3 H264 video is appropriately constrained Level 1.3 H264 video is not supported
+FAIL Level 2.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 792 but got 32400
+FAIL Level 2.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 1620 but got 32400
+FAIL Level 3.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 3600 but got 32400
+FAIL Level 3.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 5120 but got 32400
+FAIL Level 4.1 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8192 but got 32400
+FAIL Level 4.2 H264 video is appropriately constrained assert_less_than_equal: frame size expected a number less than or equal to 8704 but got 32400
+PASS Level 5.1 H264 video is appropriately constrained
+PASS Level 5.2 H264 video is appropriately constrained
+NOTRUN Level 6.1 H264 video is appropriately constrained Level 6.1 H264 video is not supported
+NOTRUN Level 6.2 H264 video is appropriately constrained Level 6.2 H264 video is not supported
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/quota-opaque-origin-expected.txt b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/quota-opaque-origin-expected.txt
new file mode 100644
index 0000000..89e22c70
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/http/tests/inspector-protocol/storage/quota-opaque-origin-expected.txt
@@ -0,0 +1,4 @@
+Tests that reading quota an opaque origin throws an error
+
+Throws an expected error: about:blank is not a valid URL
+
diff --git a/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
new file mode 100644
index 0000000..8c718e5f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/h264-profile-levels.https-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+FAIL Level 1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 3 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 4 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 5 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 6 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 1.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 1.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 1.3 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 2.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 2.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 3.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 3.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 4.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 4.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 5.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 5.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 6.1 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL Level 6.2 H264 video is appropriately constrained promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv-expected.txt
new file mode 100644
index 0000000..56516ba8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS # AUDIT TASK RUNNER STARTED.
+PASS Executing "test"
+PASS Audit report
+PASS > [test] Test convolver with real-time context
+FAIL X SNR is not greater than or equal to 77.03. Got 68.475017458378. assert_true: expected true got false
+FAIL < [test] 1 out of 1 assertions were failed. assert_true: expected true got false
+FAIL # AUDIT TASK RUNNER FINISHED: 1 out of 1 tasks were failed. assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
index 3a256b5..2a3c698 100644
--- a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
@@ -52,11 +52,22 @@
   return url;
 };
 
-const registerAttributionSrc = (t, {
+const eligibleHeader = 'Attribution-Reporting-Eligible';
+
+const registerAttributionSrc = async (t, {
   source,
   trigger,
   cookie,
+  method = 'img',
 }) => {
+  const searchParams = new URLSearchParams(location.search);
+
+  if (method === 'variant') {
+    method = searchParams.get('method');
+  }
+
+  const eligible = searchParams.get('eligible');
+
   const headers = [];
 
   if (source) {
@@ -84,8 +95,57 @@
                   }])));
   }
 
-  const img = document.createElement('img');
-  img.attributionSrc = blankURLWithHeaders(headers);
+  const url = blankURLWithHeaders(headers);
+
+  switch (method) {
+    case 'img':
+      const img = document.createElement('img');
+      if (eligible === null) {
+        img.attributionSrc = url;
+      } else {
+        img.attributionSrc = '';
+        img.src = url;
+      }
+      return 'event';
+    case 'script':
+      // TODO(apaseltiner): Support optional attributionsrc value.
+      const script = document.createElement('script');
+      script.attributionSrc = url;
+      return 'event';
+    case 'a':
+      // TODO(apaseltiner): Support optional attributionsrc value.
+      const a = document.createElement('a');
+      a.attributionSrc = url;
+      a.href = blankURL();
+      a.target = '_blank';
+      a.textContent = 'link';
+      document.body.appendChild(a);
+      await test_driver.click(a);
+      return 'navigation';
+    case 'open':
+      // TODO(apaseltiner): Support optional attributionsrc value.
+      await test_driver.bless('open window', () => {
+        open(blankURL(), '_blank', `attributionsrc=${encodeURIComponent(url)}`);
+      });
+      return 'navigation';
+    case 'fetch':
+      const headers = {};
+      if (eligible !== null) {
+        headers[eligibleHeader] = eligible;
+      }
+      await fetch(url, {headers});
+      return 'event';
+    case 'xhr':
+      const req = new XMLHttpRequest();
+      req.open('GET', url);
+      if (eligible !== null) {
+        req.setRequestHeader(eligibleHeader, eligible);
+      }
+      req.send();
+      return 'event';
+    default:
+      throw `unknown method "${method}"`;
+  }
 };
 
 /**
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-registration-test.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-registration-test.sub.https.html
new file mode 100644
index 0000000..1b5d0e7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/source-registration-test.sub.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=variant content="?method=a">
+<meta name=variant content="?method=fetch&eligible=event-source">
+<meta name=variant content="?method=img">
+<meta name=variant content="?method=img&eligible">
+<meta name=variant content="?method=open">
+<meta name=variant content="?method=script">
+<meta name=variant content="?method=xhr&eligible=event-source">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body>
+<script>
+attribution_reporting_promise_test(async t => {
+  const expectedSourceType = await registerAttributionSrc(t, {
+    source: {
+      source_event_id: '1',
+      destination: `https://{{host}}`,
+    },
+    method: 'variant',
+  });
+
+  registerAttributionSrc(t, {trigger: {
+    event_trigger_data: [{trigger_data: '0'}],
+  }});
+
+  const payload = await pollEventLevelReports();
+  assert_equals(payload.reports.length, 1);
+
+  const report = JSON.parse(payload.reports[0].body);
+  assert_equals(report.source_event_id, '1');
+  assert_equals(report.source_type, expectedSourceType);
+}, 'Source registration succeeds.');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html
index 5b49a3c..636d218 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/notification.https.html
@@ -31,5 +31,73 @@
     assert_unreached('Notification was shown; want not to be shown.' + e);
   }
 }, 'new Notification should fail inside a fenced frame');
+
+promise_test(async () => {
+  // Notification permission must be allowed or we cannot confirm that
+  // "new Notification" failed in a fenced frame.
+  await test_driver.set_permission({name: 'notifications'}, 'granted', true);
+  assert_equals(Notification.permission, 'granted');
+
+  const frame = attachFencedFrameContext();
+  const message = await frame.execute(async () => {
+    const getController = () => {
+      if (navigator.serviceWorker.controller) {
+        return navigator.serviceWorker.controller;
+      }
+      return new Promise(resolve => {
+        navigator.serviceWorker.addEventListener('controllerchange', () => {
+          resolve(navigator.serviceWorker.controller);
+        });
+      });
+    };
+
+    await navigator.serviceWorker.register(
+      'notification-sw.js', { scope: location.href });
+    const ctrl = await getController();
+
+    return new Promise(resolve => {
+      ctrl.postMessage('constructor');
+      navigator.serviceWorker.onmessage = e => {
+        resolve(e.data);
+      };
+    });
+  });
+  assert_equals(
+    message, "Failed to construct 'Notification': Illegal constructor.");
+}, 'new Notification should fail from the service worker in a fenced frame');
+
+promise_test(async () => {
+  // Notification permission must be allowed or we cannot confirm that
+  // "new Notification" failed in a fenced frame.
+  await test_driver.set_permission({name: 'notifications'}, 'granted', true);
+  assert_equals(Notification.permission, 'granted');
+
+  const frame = attachFencedFrameContext();
+  const message = await frame.execute(async () => {
+    const getController = () => {
+      if (navigator.serviceWorker.controller) {
+        return navigator.serviceWorker.controller;
+      }
+      return new Promise(resolve => {
+        navigator.serviceWorker.addEventListener('controllerchange', () => {
+          resolve(navigator.serviceWorker.controller);
+        });
+      });
+    };
+
+    await navigator.serviceWorker.register(
+      'notification-sw.js', { scope: location.href });
+    const ctrl = await getController();
+
+    return new Promise(resolve => {
+      ctrl.postMessage('showNotification');
+      navigator.serviceWorker.onmessage = e => {
+        resolve(e.data);
+      };
+    });
+  });
+  assert_equals(message,
+    "Failed to execute 'showNotification' on 'ServiceWorkerRegistration': " + "showNotification() is not allowed in fenced frames.",);
+}, 'showNotification() should fail from the service worker in a fenced frame');
 </script>
 </body>
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/notification-sw.js b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/notification-sw.js
new file mode 100644
index 0000000..e9b1e2b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/notification-sw.js
@@ -0,0 +1,20 @@
+self.addEventListener('install', e => e.waitUntil(skipWaiting()));
+self.addEventListener('activate', e => e.waitUntil(clients.claim()));
+
+self.addEventListener('message', async event => {
+  const method = event.data;
+
+  if (method === 'constructor') {
+    try {
+      new Notification('test');
+    } catch (e) {
+      event.source.postMessage(e.message);
+    }
+  } else if (method === 'showNotification') {
+    try {
+      await self.registration.showNotification('test', {body: 'test'});
+    } catch (e) {
+      event.source.postMessage(e.message);
+    }
+  }
+});
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/serviceWorker-push-sw.js b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/serviceWorker-push-sw.js
new file mode 100644
index 0000000..e344b45
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/resources/serviceWorker-push-sw.js
@@ -0,0 +1,19 @@
+self.addEventListener('install', e => e.waitUntil(skipWaiting()));
+self.addEventListener('activate', e => e.waitUntil(clients.claim()));
+
+self.addEventListener('message', async e => {
+  const method = e.data;
+
+  const promise = method === 'subscribe' ?
+      self.registration.pushManager.subscribe({userVisibleOnly: true}) :
+      Promise.resolve();
+  const message = await promise
+                      .then(() => {
+                        return `${method}: Unexpectedly started`;
+                      })
+                      .catch((e) => {
+                        return e.message;
+                      });
+
+  e.source.postMessage(message);
+});
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/serviceWorker-push.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/serviceWorker-push.https.html
new file mode 100644
index 0000000..cb460d161
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/serviceWorker-push.https.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<html>
+<head>
+<title>Service Worker: Push Messaging Test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="resources/utils.js"></script>
+</head>
+<body>
+<script>
+  promise_test(async () => {
+    const frame = attachFencedFrameContext();
+    try {
+      await frame.execute(async () => {
+        await navigator.serviceWorker.register(
+          'empty-worker.js', { scope: location.href });
+        const registration= await navigator.serviceWorker.ready;
+        return await registration.pushManager.subscribe({
+          userVisibleOnly: true
+        });
+      });
+      assert_unreached('subscribe() executed without error; want error');
+    } catch(e) {
+      assert_equals(e.message,
+        "Failed to execute 'subscribe' on 'PushManager': subscribe() is not " +
+        "allowed in fenced frames.");
+    }
+  }, 'subscribe() should fail inside a fenced frame');
+
+  promise_test(async () => {
+    const frame = attachFencedFrameContext();
+    const message = await frame.execute(async () => {
+      const getController = () => {
+        if (navigator.serviceWorker.controller) {
+          return navigator.serviceWorker.controller;
+        }
+        return new Promise(resolve => {
+          navigator.serviceWorker.addEventListener('controllerchange', () => {
+            resolve(navigator.serviceWorker.controller);
+          });
+        });
+      };
+      await navigator.serviceWorker.register(
+        'serviceWorker-push-sw.js', { scope: location.href });
+      return new Promise(async resolve => {
+        const ctrl = await getController();
+        ctrl.postMessage('subscribe');
+        navigator.serviceWorker.onmessage = e => {
+          resolve(e.data);
+        }
+      });
+    });
+    assert_equals(message, "Failed to execute 'subscribe' on " +
+    "'PushManager': subscribe() is not allowed in fenced frames.");
+  }, 'subscribe() should fail from the service worker inside a fenced frame');
+</script>
+</body>
+</html>
diff --git a/third_party/closure_compiler/externs/input_method_private.js b/third_party/closure_compiler/externs/input_method_private.js
index 3dc1cf4..df961b2 100644
--- a/third_party/closure_compiler/externs/input_method_private.js
+++ b/third_party/closure_compiler/externs/input_method_private.js
@@ -437,8 +437,13 @@
  * }): void} callback Called with screen coordinates of the text field when the
  *     operation completes. On failure, $(ref:runtime.lastError) is set.
  */
-chrome.inputMethodPrivate.getTextFieldBounds = function(
-    parameters, callback) {};
+chrome.inputMethodPrivate.getTextFieldBounds = function(parameters, callback) {};
+
+/**
+ * Fired when the caret bounds change.
+ * @type {!ChromeEvent}
+ */
+chrome.inputMethodPrivate.onCaretBoundsChanged;
 
 /**
  * Fired when the input method is changed.
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 3efdc14e..d0c37db 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 6e946c4af851722b9d444af89590d65cd2c3ec38
+Revision: 816c5572b8c6d7aac8fb85db770496bc1f1da439
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 4a20125..2299223 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -14,7 +14,6 @@
 
 vars = {
   'chromium_git': 'https://chromium.googlesource.com',
-  'gn_version': 'git_revision:2ecd43a10266bd091c98e6dcde507c64f6a0dad3',
   'pull_linux_clang': False,
   'pull_win_toolchain': False,
   # Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of
@@ -26,11 +25,7 @@
 deps = {
   'buildtools':
       Var('chromium_git') + '/chromium/src/buildtools.git@' +
-      '8b16338d17cd71b04a6ba28da7322ab6739892c2',
-  'buildtools/clang_format/script':
-      Var('chromium_git') +
-      '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +
-      'c912837e0d82b5ca4b6e790b573b3956d3744c1c',
+      '9e121212d42be62a7cce38072f925f8398d11e49',
   'crashpad/third_party/edo/edo': {
       'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' +
       '727e556705278598fce683522beedbb9946bfda0',
@@ -52,37 +47,7 @@
       Var('chromium_git') + '/chromium/src/third_party/zlib@' +
       '13dc246a58e4b72104d35f9b1809af95221ebda7',
 
-  # CIPD packages.
-  'buildtools/linux64': {
-    'packages': [
-      {
-        'package': 'gn/gn/linux-amd64',
-        'version': Var('gn_version'),
-      }
-    ],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "linux"',
-  },
-  'buildtools/mac': {
-    'packages': [
-      {
-        'package': 'gn/gn/mac-${{arch}}',
-        'version': Var('gn_version'),
-      }
-    ],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "mac"',
-  },
-  'buildtools/win': {
-    'packages': [
-      {
-        'package': 'gn/gn/windows-amd64',
-        'version': Var('gn_version'),
-      }
-    ],
-    'dep_type': 'cipd',
-    'condition': 'host_os == "win"',
-  },
+  # CIPD packages below.
   'crashpad/third_party/linux/clang/linux-amd64': {
     'packages': [
       {
@@ -160,9 +125,7 @@
       '--no_auth',
       '--bucket=chromium-clang-format',
       '--sha1_file',
-      'buildtools/mac/clang-format.{host_cpu}.sha1',
-      '--output',
-      'buildtools/mac/clang-format',
+      'buildtools/mac/clang-format.sha1',
     ],
   },
   {
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_generic.cc b/third_party/crashpad/crashpad/client/crash_report_database_generic.cc
index 742850a..c4e040109 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_generic.cc
+++ b/third_party/crashpad/crashpad/client/crash_report_database_generic.cc
@@ -18,7 +18,6 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <mutex>
 #include <tuple>
 #include <utility>
 
@@ -259,15 +258,15 @@
   static bool WriteMetadata(const base::FilePath& path, const Report& report);
 
   Settings& SettingsInternal() {
-    std::call_once(settings_init_, [this]() {
+    if (!settings_init_)
       settings_.Initialize(base_dir_.Append(kSettings));
-    });
+    settings_init_ = true;
     return settings_;
   }
 
   base::FilePath base_dir_;
   Settings settings_;
-  std::once_flag settings_init_;
+  bool settings_init_ = false;
   InitializationStateDcheck initialized_;
 };
 
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
index e33b932..4c9e5e7 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
+++ b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -27,7 +27,6 @@
 
 #include <array>
 #include <iterator>
-#include <mutex>
 #include <tuple>
 
 #include "base/logging.h"
@@ -264,15 +263,15 @@
   void CleanOrphanedAttachments();
 
   Settings& SettingsInternal() {
-    std::call_once(settings_init_, [this]() {
+    if (!settings_init_)
       settings_.Initialize(base_dir_.Append(kSettings));
-    });
+    settings_init_ = true;
     return settings_;
   }
 
   base::FilePath base_dir_;
   Settings settings_;
-  std::once_flag settings_init_;
+  bool settings_init_;
   bool xattr_new_names_;
   InitializationStateDcheck initialized_;
 };
@@ -281,7 +280,7 @@
     : CrashReportDatabase(),
       base_dir_(path),
       settings_(),
-      settings_init_(),
+      settings_init_(false),
       xattr_new_names_(false),
       initialized_() {}
 
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_win.cc b/third_party/crashpad/crashpad/client/crash_report_database_win.cc
index e111d8e..e16aa10 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_win.cc
+++ b/third_party/crashpad/crashpad/client/crash_report_database_win.cc
@@ -21,7 +21,6 @@
 #include <time.h>
 #include <wchar.h>
 
-#include <mutex>
 #include <tuple>
 #include <utility>
 
@@ -664,15 +663,15 @@
   std::unique_ptr<Metadata> AcquireMetadata();
 
   Settings& SettingsInternal() {
-    std::call_once(settings_init_, [this]() {
+    if (!settings_init_)
       settings_.Initialize(base_dir_.Append(kSettings));
-    });
+    settings_init_ = true;
     return settings_;
   }
 
   base::FilePath base_dir_;
   Settings settings_;
-  std::once_flag settings_init_;
+  bool settings_init_;
   InitializationStateDcheck initialized_;
 };
 
@@ -680,7 +679,7 @@
     : CrashReportDatabase(),
       base_dir_(path),
       settings_(),
-      settings_init_(),
+      settings_init_(false),
       initialized_() {}
 
 CrashReportDatabaseWin::~CrashReportDatabaseWin() {
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
index e7e9003..2869c21 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
@@ -811,22 +811,6 @@
       CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_BASIC_INFO");
     }
 
-    thread_extended_info extended_info;
-    count = THREAD_EXTENDED_INFO_COUNT;
-    kr = thread_info(thread,
-                     THREAD_EXTENDED_INFO,
-                     reinterpret_cast<thread_info_t>(&extended_info),
-                     &count);
-    if (kr == KERN_SUCCESS) {
-      WritePropertyBytes(
-          writer,
-          IntermediateDumpKey::kThreadName,
-          reinterpret_cast<const void*>(extended_info.pth_name),
-          strnlen(extended_info.pth_name, sizeof(extended_info.pth_name)));
-    } else {
-      CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_EXTENDED_INFO");
-    }
-
     thread_precedence_policy precedence;
     count = THREAD_PRECEDENCE_POLICY_COUNT;
     boolean_t get_default = FALSE;
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
index f1d8a54..bccc930 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc
@@ -26,7 +26,6 @@
 #include "client/simple_string_dictionary.h"
 #include "gtest/gtest.h"
 #include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
-#include "test/scoped_set_thread_name.h"
 #include "test/scoped_temp_dir.h"
 #include "test/test_paths.h"
 #include "util/file/filesystem.h"
@@ -207,8 +206,6 @@
 }
 
 TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {
-  const ScopedSetThreadName scoped_set_thread_name("TestThreads");
-
   WriteReport();
   internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
   ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));
@@ -224,7 +221,6 @@
                         &count),
             0);
   EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id);
-  EXPECT_EQ(threads[0]->ThreadName(), "TestThreads");
 }
 
 TEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) {
diff --git a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
index bc5bf02..43abca8 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_file_writer.cc
@@ -26,7 +26,6 @@
 #include "minidump/minidump_module_writer.h"
 #include "minidump/minidump_system_info_writer.h"
 #include "minidump/minidump_thread_id_map.h"
-#include "minidump/minidump_thread_name_list_writer.h"
 #include "minidump/minidump_thread_writer.h"
 #include "minidump/minidump_unloaded_module_writer.h"
 #include "minidump/minidump_user_extension_stream_data_source.h"
@@ -35,7 +34,6 @@
 #include "snapshot/exception_snapshot.h"
 #include "snapshot/module_snapshot.h"
 #include "snapshot/process_snapshot.h"
-#include "snapshot/thread_snapshot.h"
 #include "util/file/file_writer.h"
 #include "util/numeric/safe_assignment.h"
 
@@ -96,21 +94,6 @@
   add_stream_result = AddStream(std::move(thread_list));
   DCHECK(add_stream_result);
 
-  bool has_thread_name = false;
-  for (const ThreadSnapshot* thread_snapshot : process_snapshot->Threads()) {
-    if (!thread_snapshot->ThreadName().empty()) {
-      has_thread_name = true;
-      break;
-    }
-  }
-  if (has_thread_name) {
-    auto thread_name_list = std::make_unique<MinidumpThreadNameListWriter>();
-    thread_name_list->InitializeFromSnapshot(process_snapshot->Threads(),
-                                             thread_id_map);
-    add_stream_result = AddStream(std::move(thread_name_list));
-    DCHECK(add_stream_result);
-  }
-
   const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception();
   if (exception_snapshot) {
     auto exception = std::make_unique<MinidumpExceptionWriter>();
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc
index 6f49f0c..a955c27 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.cc
@@ -17,47 +17,21 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "minidump/minidump_thread_id_map.h"
-#include "snapshot/thread_snapshot.h"
 #include "util/file/file_writer.h"
 #include "util/numeric/safe_assignment.h"
 
 namespace crashpad {
 
 MinidumpThreadNameWriter::MinidumpThreadNameWriter()
-    : MinidumpWritable(), rva_of_thread_name_(), thread_id_(), name_() {}
+    : MinidumpWritable(), thread_name_(), name_() {}
 
 MinidumpThreadNameWriter::~MinidumpThreadNameWriter() {}
 
-void MinidumpThreadNameWriter::InitializeFromSnapshot(
-    const ThreadSnapshot* thread_snapshot,
-    const MinidumpThreadIDMap& thread_id_map) {
-  DCHECK_EQ(state(), kStateMutable);
-
-  const auto it = thread_id_map.find(thread_snapshot->ThreadID());
-  DCHECK(it != thread_id_map.end());
-  SetThreadId(it->second);
-  SetThreadName(thread_snapshot->ThreadName());
-}
-
-RVA64 MinidumpThreadNameWriter::RvaOfThreadName() const {
+const MINIDUMP_THREAD_NAME* MinidumpThreadNameWriter::MinidumpThreadName()
+    const {
   DCHECK_EQ(state(), kStateWritable);
 
-  return rva_of_thread_name_;
-}
-
-uint32_t MinidumpThreadNameWriter::ThreadId() const {
-  DCHECK_EQ(state(), kStateWritable);
-
-  return thread_id_;
-}
-
-bool MinidumpThreadNameWriter::Freeze() {
-  DCHECK_EQ(state(), kStateMutable);
-
-  name_->RegisterRVA(&rva_of_thread_name_);
-
-  return MinidumpWritable::Freeze();
+  return &thread_name_;
 }
 
 void MinidumpThreadNameWriter::SetThreadName(const std::string& name) {
@@ -72,9 +46,10 @@
 size_t MinidumpThreadNameWriter::SizeOfObject() {
   DCHECK_GE(state(), kStateFrozen);
 
-  // This object doesn’t directly write anything itself. Its parent writes the
-  // MINIDUMP_THREAD_NAME objects as part of a MINIDUMP_THREAD_NAME_LIST, and
-  // its children are responsible for writing themselves.
+  // This object doesn’t directly write anything itself. Its
+  // MINIDUMP_THREAD_NAME is written by its parent as part of a
+  // MINIDUMP_THREAD_NAME_LIST, and its children are responsible for writing
+  // themselves.
   return 0;
 }
 
@@ -88,6 +63,24 @@
   return children;
 }
 
+bool MinidumpThreadNameWriter::WillWriteAtOffsetImpl(FileOffset offset) {
+  DCHECK_EQ(state(), kStateFrozen);
+
+  // This cannot use RegisterRVA(&thread_name_.RvaOfThreadName), since
+  // &MINIDUMP_THREAD_NAME_LIST::RvaOfThreadName is not aligned on a pointer
+  // boundary, so it causes failures on 32-bit ARM.
+  //
+  // Instead, manually update the RVA64 to the current file offset since the
+  // child thread_name_ will write its contents at that offset.
+  decltype(thread_name_.RvaOfThreadName) local_rva_of_thread_name;
+  if (!AssignIfInRange(&local_rva_of_thread_name, offset)) {
+    LOG(ERROR) << "offset " << offset << " out of range";
+    return false;
+  }
+  thread_name_.RvaOfThreadName = local_rva_of_thread_name;
+  return MinidumpWritable::WillWriteAtOffsetImpl(offset);
+}
+
 bool MinidumpThreadNameWriter::WriteObject(FileWriterInterface* file_writer) {
   DCHECK_EQ(state(), kStateWritable);
 
@@ -103,19 +96,6 @@
 
 MinidumpThreadNameListWriter::~MinidumpThreadNameListWriter() {}
 
-void MinidumpThreadNameListWriter::InitializeFromSnapshot(
-    const std::vector<const ThreadSnapshot*>& thread_snapshots,
-    const MinidumpThreadIDMap& thread_id_map) {
-  DCHECK_EQ(state(), kStateMutable);
-  DCHECK(thread_names_.empty());
-
-  for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {
-    auto thread = std::make_unique<MinidumpThreadNameWriter>();
-    thread->InitializeFromSnapshot(thread_snapshot, thread_id_map);
-    AddThreadName(std::move(thread));
-  }
-}
-
 void MinidumpThreadNameListWriter::AddThreadName(
     std::unique_ptr<MinidumpThreadNameWriter> thread_name) {
   DCHECK_EQ(state(), kStateMutable);
@@ -170,15 +150,10 @@
   std::vector<WritableIoVec> iovecs(1, iov);
   iovecs.reserve(thread_names_.size() + 1);
 
-  std::vector<MINIDUMP_THREAD_NAME> minidump_thread_names;
-  minidump_thread_names.reserve(thread_names_.size());
   for (const auto& thread_name : thread_names_) {
-    auto& minidump_thread_name = minidump_thread_names.emplace_back();
-    minidump_thread_name.ThreadId = thread_name->ThreadId();
-    minidump_thread_name.RvaOfThreadName = thread_name->RvaOfThreadName();
-    iov.iov_base = &minidump_thread_name;
-    iov.iov_len = sizeof(minidump_thread_name);
-    iovecs.push_back(iov);
+    iov.iov_base = thread_name->MinidumpThreadName();
+    iov.iov_len = sizeof(MINIDUMP_THREAD_NAME);
+    iovecs.emplace_back(iov);
   }
 
   return file_writer->WriteIoVec(&iovecs);
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h
index f573111c..5afab981 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h
+++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer.h
@@ -25,7 +25,6 @@
 
 #include "minidump/minidump_stream_writer.h"
 #include "minidump/minidump_string_writer.h"
-#include "minidump/minidump_thread_id_map.h"
 #include "minidump/minidump_writable.h"
 
 namespace crashpad {
@@ -46,57 +45,28 @@
 
   ~MinidumpThreadNameWriter() override;
 
-  //! \brief Initializes the MINIDUMP_THREAD_NAME based on \a thread_snapshot.
+  //! \brief Returns a MINIDUMP_THREAD_NAME referencing this object’s data.
   //!
-  //! \param[in] thread_snapshot The thread snapshot to use as source data.
-  //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to
-  //!     determine the 32-bit minidump thread ID to use for \a thread_snapshot.
-  //!
-  //! \note Valid in #kStateMutable.
-  void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot,
-                              const MinidumpThreadIDMap& thread_id_map);
-
-  //! \brief Sets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId.
-  void SetThreadId(uint32_t thread_id) { thread_id_ = thread_id; }
-
-  //! \brief Gets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId.
+  //! This method is expected to be called by a MinidumpThreadNameListWriter in
+  //! order to obtain a MINIDUMP_THREAD_NAME to include in its list.
   //!
   //! \note Valid in #kStateWritable.
-  uint32_t ThreadId() const;
+  const MINIDUMP_THREAD_NAME* MinidumpThreadName() const;
+
+  //! \brief Sets MINIDUMP_THREAD_NAME::ThreadId.
+  void SetThreadId(uint32_t thread_id) { thread_name_.ThreadId = thread_id; }
 
   //! \brief Sets MINIDUMP_THREAD_NAME::RvaOfThreadName.
   void SetThreadName(const std::string& thread_name);
 
-  //! \brief Returns an RVA64 which has been updated with the relative address
-  //!    of the thread name.
-  //!
-  //! This method is expected to be called by a MinidumpThreadNameListWriter in
-  //! order to obtain the RVA64 of the thread name.
-  //!
-  //! \note Valid in #kStateWritable.
-  RVA64 RvaOfThreadName() const;
-
  private:
   // MinidumpWritable:
-  bool Freeze() override;
   size_t SizeOfObject() override;
   std::vector<MinidumpWritable*> Children() override;
+  bool WillWriteAtOffsetImpl(FileOffset offset) override;
   bool WriteObject(FileWriterInterface* file_writer) override;
 
-  // This exists as a separate field so MinidumpWritable::RegisterRVA() can be
-  // used on a guaranteed-aligned pointer (MINIDUMP_THREAD_NAME::RvaOfThreadName
-  // is not 64-bit aligned, causing issues on ARM).
-  RVA64 rva_of_thread_name_;
-
-  // Although this class manages the data for a MINIDUMP_THREAD_NAME, it does
-  // not directly hold a MINIDUMP_THREAD_NAME, as that struct contains a
-  // non-aligned RVA64 field which prevents it use with
-  // MinidumpWritable::RegisterRVA().
-  //
-  // Instead, this class individually holds the fields of the
-  // MINIDUMP_THREAD_NAME which are fetched by MinidumpThreadNameListWriter.
-  uint32_t thread_id_;
-
+  MINIDUMP_THREAD_NAME thread_name_;
   std::unique_ptr<internal::MinidumpUTF16StringWriter> name_;
 };
 
@@ -113,18 +83,6 @@
 
   ~MinidumpThreadNameListWriter() override;
 
-  //! \brief Adds an initialized MINIDUMP_THREAD_NAME for each thread in \a
-  //!     thread_snapshots to the MINIDUMP_THREAD_NAME_LIST.
-  //!
-  //! \param[in] thread_snapshots The thread snapshots to use as source data.
-  //! \param[in] thread_id_map A MinidumpThreadIDMap previously built by
-  //!     MinidumpThreadListWriter::InitializeFromSnapshot().
-  //!
-  //! \note Valid in #kStateMutable.
-  void InitializeFromSnapshot(
-      const std::vector<const ThreadSnapshot*>& thread_snapshots,
-      const MinidumpThreadIDMap& thread_id_map);
-
   //! \brief Adds a MinidumpThreadNameWriter to the MINIDUMP_THREAD_LIST.
   //!
   //! This object takes ownership of \a thread_name and becomes its parent in
diff --git a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc
index 265c12e..b61b1bb 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_thread_name_list_writer_test.cc
@@ -24,7 +24,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "gtest/gtest.h"
 #include "minidump/minidump_file_writer.h"
-#include "minidump/minidump_system_info_writer.h"
 #include "minidump/test/minidump_file_writer_test_util.h"
 #include "minidump/test/minidump_string_writer_test_util.h"
 #include "minidump/test/minidump_writable_test_util.h"
@@ -138,64 +137,6 @@
                                            kThreadName));
 }
 
-TEST(MinidumpThreadNameListWriter, OneThreadWithLeadingPadding) {
-  MinidumpFileWriter minidump_file_writer;
-
-  // Add a stream before the MINIDUMP_THREAD_NAME_LIST to ensure the thread name
-  // MINIDUMP_STRING requires leading padding to align to a 4-byte boundary.
-  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();
-  system_info_writer->SetCSDVersion("");
-  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));
-
-  auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
-
-  constexpr uint32_t kThreadID = 0x11111111;
-  const std::string kThreadName = "ariadne";
-
-  auto thread_name_list_writer =
-      std::make_unique<MinidumpThreadNameListWriter>();
-  auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
-  thread_name_writer->SetThreadId(kThreadID);
-  thread_name_writer->SetThreadName(kThreadName);
-  thread_name_list_writer->AddThreadName(std::move(thread_name_writer));
-
-  ASSERT_TRUE(
-      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
-
-  StringFile string_file;
-  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
-
-  ASSERT_GT(string_file.string().size(),
-            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
-                sizeof(MINIDUMP_THREAD_NAME_LIST) +
-                1 * sizeof(MINIDUMP_THREAD_NAME));
-
-  const uint32_t kExpectedStreams = 2;
-  const MINIDUMP_DIRECTORY* directory;
-  const MINIDUMP_HEADER* header =
-      MinidumpHeaderAtStart(string_file.string(), &directory);
-  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));
-  ASSERT_TRUE(directory);
-
-  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);
-  ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeThreadNameList);
-
-  const MINIDUMP_THREAD_NAME_LIST* thread_name_list =
-      MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(
-          string_file.string(), directory[1].Location);
-  ASSERT_TRUE(thread_name_list);
-
-  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u);
-
-  MINIDUMP_THREAD_NAME expected = {};
-  expected.ThreadId = kThreadID;
-
-  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
-                                           &thread_name_list->ThreadNames[0],
-                                           string_file.string(),
-                                           kThreadName));
-}
-
 TEST(MinidumpThreadNameListWriter, TwoThreads_DifferentNames) {
   MinidumpFileWriter minidump_file_writer;
   auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
index b8e71afb..362be5b 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc
@@ -22,10 +22,8 @@
 
 #include <iterator>
 
-#include "base/strings/stringprintf.h"
 #include "gtest/gtest.h"
 #include "test/multiprocess_exec.h"
-#include "test/scoped_set_thread_name.h"
 #include "test/test_paths.h"
 #include "util/fuchsia/scoped_task_suspend.h"
 
@@ -34,8 +32,6 @@
 namespace {
 
 TEST(ProcessReaderFuchsia, SelfBasic) {
-  const ScopedSetThreadName scoped_set_thread_name("SelfBasic");
-
   ProcessReaderFuchsia process_reader;
   ASSERT_TRUE(process_reader.Initialize(*zx::process::self()));
 
@@ -79,7 +75,7 @@
             ZX_OK);
   EXPECT_EQ(threads[0].id, info.koid);
   EXPECT_EQ(threads[0].state, ZX_THREAD_STATE_RUNNING);
-  EXPECT_EQ(threads[0].name, "SelfBasic");
+  EXPECT_EQ(threads[0].name, "initial-thread");
 }
 
 constexpr char kTestMemory[] = "Read me from another process";
@@ -122,44 +118,27 @@
   test.Run();
 }
 
-struct ThreadData {
-  zx_handle_t port;
-  std::string name;
-};
-
 void* SignalAndSleep(void* arg) {
-  const ThreadData* thread_data = reinterpret_cast<const ThreadData*>(arg);
-  const ScopedSetThreadName scoped_set_thread_name(thread_data->name);
   zx_port_packet_t packet = {};
   packet.type = ZX_PKT_TYPE_USER;
-  zx_port_queue(thread_data->port, &packet);
+  zx_port_queue(*reinterpret_cast<zx_handle_t*>(arg), &packet);
   zx_nanosleep(ZX_TIME_INFINITE);
   return nullptr;
 }
 
 CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) {
-  const ScopedSetThreadName scoped_set_thread_name(
-      "ProcessReaderChildThreadsTest-Main");
-
   // Create 5 threads with stack sizes of 4096, 8192, ...
   zx_handle_t port;
   zx_status_t status = zx_port_create(0, &port);
   EXPECT_EQ(status, ZX_OK);
 
   constexpr size_t kNumThreads = 5;
-  struct ThreadData thread_data[kNumThreads] = {{0, 0}};
-
   for (size_t i = 0; i < kNumThreads; ++i) {
-    thread_data[i] = {
-        .port = port,
-        .name = base::StringPrintf("ProcessReaderChildThreadsTest-%zu", i + 1),
-    };
     pthread_attr_t attr;
     EXPECT_EQ(pthread_attr_init(&attr), 0);
     EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0);
     pthread_t thread;
-    EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &thread_data[i]),
-              0);
+    EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &port), 0);
   }
 
   // Wait until all threads are ready.
@@ -200,14 +179,10 @@
     const auto& threads = process_reader.Threads();
     EXPECT_EQ(threads.size(), 6u);
 
-    EXPECT_EQ(threads[0].name, "ProcessReaderChildThreadsTest-main");
-
     for (size_t i = 1; i < 6; ++i) {
       ASSERT_GT(threads[i].stack_regions.size(), 0u);
       EXPECT_GT(threads[i].stack_regions[0].size(), 0u);
       EXPECT_LE(threads[i].stack_regions[0].size(), i * 4096u);
-      EXPECT_EQ(threads[i].name,
-                base::StringPrintf("ProcessReaderChildThreadsTest-%zu", i));
     }
   }
 };
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc
index b12ee861..369203a 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc
@@ -25,7 +25,6 @@
       context_arch_(),
       context_(),
       stack_(),
-      thread_name_(),
       thread_id_(ZX_KOID_INVALID),
       thread_specific_data_address_(0),
       initialized_() {}
@@ -61,7 +60,6 @@
     // TODO(scottmg): Handle split stack by adding other parts to ExtraMemory().
   }
 
-  thread_name_ = thread.name;
   thread_id_ = thread.id;
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
@@ -83,11 +81,6 @@
   return thread_id_;
 }
 
-std::string ThreadSnapshotFuchsia::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_name_;
-}
-
 int ThreadSnapshotFuchsia::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   // There is not (currently) a suspend count for threads on Fuchsia.
diff --git a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h
index 5c804fb1..b91a514d 100644
--- a/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h
+++ b/third_party/crashpad/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h
@@ -18,8 +18,6 @@
 #include <stdint.h>
 #include <zircon/types.h>
 
-#include <string>
-
 #include "build/build_config.h"
 #include "snapshot/cpu_context.h"
 #include "snapshot/fuchsia/process_reader_fuchsia.h"
@@ -58,7 +56,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -74,7 +71,6 @@
 #endif
   CPUContext context_;
   MemorySnapshotGeneric stack_;
-  std::string thread_name_;
   zx_koid_t thread_id_;
   zx_vaddr_t thread_specific_data_address_;
   InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
index 0a170e7..34c34f8 100644
--- a/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
@@ -392,7 +392,6 @@
               Key::kThreadContextMemoryRegionData, "string", 6));
         }
       }
-      EXPECT_TRUE(writer->AddPropertyBytes(Key::kThreadName, "ariadne", 7));
     }
   }
 
@@ -412,7 +411,6 @@
     uint64_t thread_id = 1;
     for (auto thread : threads) {
       EXPECT_EQ(thread->ThreadID(), thread_id);
-      EXPECT_EQ(thread->ThreadName(), "ariadne");
       EXPECT_EQ(thread->SuspendCount(), 666);
       EXPECT_EQ(thread->Priority(), 5);
       EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++);
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
index 40387fa..ed7b28a 100644
--- a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
+++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
@@ -75,7 +75,6 @@
 #endif
       context_(),
       stack_(),
-      thread_name_(),
       thread_id_(0),
       thread_specific_data_address_(0),
       suspend_count_(0),
@@ -101,7 +100,6 @@
   GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_);
   GetDataValueFromMap(
       thread_data, Key::kThreadDataAddress, &thread_specific_data_address_);
-  GetDataStringFromMap(thread_data, Key::kThreadName, &thread_name_);
 
 #if defined(ARCH_CPU_X86_64)
   typedef x86_thread_state64_t thread_state_type;
@@ -220,11 +218,6 @@
   return thread_id_;
 }
 
-std::string ThreadSnapshotIOSIntermediateDump::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_name_;
-}
-
 int ThreadSnapshotIOSIntermediateDump::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h
index dafa455..cf9ccec 100644
--- a/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h
+++ b/third_party/crashpad/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h
@@ -15,8 +15,6 @@
 #ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
 #define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
 
-#include <string>
-
 #include "build/build_config.h"
 #include "snapshot/cpu_context.h"
 #include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
@@ -51,7 +49,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -68,7 +65,6 @@
   CPUContext context_;
   std::vector<uint8_t> exception_stack_memory_;
   MemorySnapshotIOSIntermediateDump stack_;
-  std::string thread_name_;
   uint64_t thread_id_;
   uint64_t thread_specific_data_address_;
   int suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc
index 4b663bb..5711f34 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.cc
@@ -24,7 +24,6 @@
 #include <algorithm>
 
 #include "base/logging.h"
-#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "snapshot/linux/debug_rendezvous.h"
 #include "util/linux/auxiliary_vector.h"
@@ -53,7 +52,6 @@
     : thread_info(),
       stack_region_address(0),
       stack_region_size(0),
-      name(),
       tid(-1),
       static_priority(-1),
       nice_value(-1) {}
@@ -66,23 +64,6 @@
     return false;
   }
 
-  // From man proc(5):
-  //
-  // /proc/[pid]/comm (since Linux 2.6.33)
-  //
-  // Different threads in the same process may have different comm values,
-  // accessible via /proc/[pid]/task/[tid]/comm.
-  const std::string path = base::StringPrintf(
-      "/proc/%d/task/%d/comm", connection->GetProcessID(), tid);
-  if (connection->ReadFileContents(base::FilePath(path), &name)) {
-    if (!name.empty() && name.back() == '\n') {
-      // Remove the final newline character.
-      name.pop_back();
-    }
-  } else {
-    // Continue on without the thread name.
-  }
-
   // TODO(jperaza): Collect scheduling priorities via the broker when they can't
   // be collected directly.
   have_priorities = false;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h
index e8cf107..f44e15f 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h
+++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux.h
@@ -60,7 +60,6 @@
     ThreadInfo thread_info;
     LinuxVMAddress stack_region_address;
     LinuxVMSize stack_region_size;
-    std::string name;
     pid_t tid;
     int sched_policy;
     int static_priority;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
index e4179de..81b3d6e 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_linux_test.cc
@@ -43,7 +43,6 @@
 #include "test/linux/get_tls.h"
 #include "test/multiprocess.h"
 #include "test/scoped_module_handle.h"
-#include "test/scoped_set_thread_name.h"
 #include "test/test_paths.h"
 #include "util/file/file_io.h"
 #include "util/file/file_writer.h"
@@ -170,9 +169,7 @@
 
   void StartThreads(size_t thread_count, size_t stack_size = 0) {
     for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
-      const std::string thread_name =
-          base::StringPrintf("ThreadPool-%zu", thread_index);
-      threads_.push_back(std::make_unique<Thread>(thread_name));
+      threads_.push_back(std::make_unique<Thread>());
       Thread* thread = threads_.back().get();
 
       pthread_attr_t attr;
@@ -214,26 +211,22 @@
   }
 
   pid_t GetThreadExpectation(size_t thread_index,
-                             ThreadExpectation* expectation,
-                             std::string* thread_name_expectation) {
+                             ThreadExpectation* expectation) {
     CHECK_LT(thread_index, threads_.size());
 
     const Thread* thread = threads_[thread_index].get();
     *expectation = thread->expectation;
-    *thread_name_expectation = thread->name;
     return thread->tid;
   }
 
  private:
   struct Thread {
-    explicit Thread(const std::string& name)
+    Thread()
         : pthread(),
           expectation(),
           ready_semaphore(0),
           exit_semaphore(0),
-          tid(-1),
-          name(name) {
-    }
+          tid(-1) {}
     ~Thread() {}
 
     pthread_t pthread;
@@ -242,12 +235,10 @@
     Semaphore ready_semaphore;
     Semaphore exit_semaphore;
     pid_t tid;
-    const std::string name;
   };
 
   static void* ThreadMain(void* argument) {
     Thread* thread = static_cast<Thread*>(argument);
-    const ScopedSetThreadName scoped_set_thread_name(thread->name);
 
     CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0)
         << ErrnoMessage("setpriority");
@@ -269,24 +260,20 @@
 };
 
 using ThreadMap = std::map<pid_t, TestThreadPool::ThreadExpectation>;
-using ThreadNameMap = std::map<pid_t, std::string>;
 
 void ExpectThreads(const ThreadMap& thread_map,
-                   const ThreadNameMap& thread_name_map,
                    const std::vector<ProcessReaderLinux::Thread>& threads,
                    PtraceConnection* connection) {
   ASSERT_EQ(threads.size(), thread_map.size());
-  ASSERT_EQ(threads.size(), thread_name_map.size());
 
   MemoryMap memory_map;
   ASSERT_TRUE(memory_map.Initialize(connection));
 
   for (const auto& thread : threads) {
     SCOPED_TRACE(
-        base::StringPrintf("Thread id %d, name %s, tls 0x%" PRIx64
+        base::StringPrintf("Thread id %d, tls 0x%" PRIx64
                            ", stack addr 0x%" PRIx64 ", stack size 0x%" PRIx64,
                            thread.tid,
-                           thread.name.c_str(),
                            thread.thread_info.thread_specific_data_address,
                            thread.stack_region_address,
                            thread.stack_region_size));
@@ -319,10 +306,6 @@
     EXPECT_EQ(thread.sched_policy, iterator->second.sched_policy);
     EXPECT_EQ(thread.static_priority, iterator->second.static_priority);
     EXPECT_EQ(thread.nice_value, iterator->second.nice_value);
-
-    const auto& thread_name_iterator = thread_name_map.find(thread.tid);
-    ASSERT_NE(thread_name_iterator, thread_name_map.end());
-    EXPECT_EQ(thread.name, thread_name_iterator->second);
   }
 }
 
@@ -339,7 +322,6 @@
  private:
   void MultiprocessParent() override {
     ThreadMap thread_map;
-    ThreadNameMap thread_name_map;
     for (size_t thread_index = 0; thread_index < kThreadCount + 1;
          ++thread_index) {
       pid_t tid;
@@ -349,14 +331,6 @@
       CheckedReadFileExactly(
           ReadPipeHandle(), &expectation, sizeof(expectation));
       thread_map[tid] = expectation;
-
-      std::string::size_type thread_name_length;
-      CheckedReadFileExactly(
-          ReadPipeHandle(), &thread_name_length, sizeof(thread_name_length));
-      std::string thread_name(thread_name_length, '\0');
-      CheckedReadFileExactly(
-          ReadPipeHandle(), thread_name.data(), thread_name_length);
-      thread_name_map[tid] = thread_name;
     }
 
     DirectPtraceConnection connection;
@@ -366,22 +340,19 @@
     ASSERT_TRUE(process_reader.Initialize(&connection));
     const std::vector<ProcessReaderLinux::Thread>& threads =
         process_reader.Threads();
-    ExpectThreads(thread_map, thread_name_map, threads, &connection);
+    ExpectThreads(thread_map, threads, &connection);
   }
 
   void MultiprocessChild() override {
     TestThreadPool thread_pool;
     thread_pool.StartThreads(kThreadCount, stack_size_);
 
-    const std::string current_thread_name = "MultiprocChild";
-    const ScopedSetThreadName scoped_set_thread_name(current_thread_name);
-
     TestThreadPool::ThreadExpectation expectation;
 #if defined(MEMORY_SANITIZER)
     // memset() + re-initialization is required to zero padding bytes for MSan.
     memset(&expectation, 0, sizeof(expectation));
 #endif  // defined(MEMORY_SANITIZER)
-    expectation = {0};
+    expectation = {};
     expectation.tls = GetTLS();
     expectation.stack_address = reinterpret_cast<LinuxVMAddress>(&thread_pool);
 
@@ -402,28 +373,11 @@
 
     CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid));
     CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation));
-    const std::string::size_type current_thread_name_length =
-        current_thread_name.length();
-    CheckedWriteFile(WritePipeHandle(),
-                     &current_thread_name_length,
-                     sizeof(current_thread_name_length));
-    CheckedWriteFile(WritePipeHandle(),
-                     current_thread_name.data(),
-                     current_thread_name_length);
 
     for (size_t thread_index = 0; thread_index < kThreadCount; ++thread_index) {
-      std::string thread_name_expectation;
-      tid = thread_pool.GetThreadExpectation(
-          thread_index, &expectation, &thread_name_expectation);
+      tid = thread_pool.GetThreadExpectation(thread_index, &expectation);
       CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid));
       CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation));
-      const std::string::size_type thread_name_length =
-          thread_name_expectation.length();
-      CheckedWriteFile(
-          WritePipeHandle(), &thread_name_length, sizeof(thread_name_length));
-      CheckedWriteFile(WritePipeHandle(),
-                       thread_name_expectation.data(),
-                       thread_name_length);
     }
 
     CheckedReadFileAtEOF(ReadPipeHandle());
diff --git a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
index 04776de..f279e0ad 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
+++ b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
@@ -133,7 +133,6 @@
       context_(),
       stack_(),
       thread_specific_data_address_(0),
-      thread_name_(),
       thread_id_(-1),
       priority_(-1),
       initialized_() {}
@@ -201,7 +200,6 @@
   thread_specific_data_address_ =
       thread.thread_info.thread_specific_data_address;
 
-  thread_name_ = thread.name;
   thread_id_ = thread.tid;
 
   priority_ =
@@ -236,11 +234,6 @@
   return thread_id_;
 }
 
-std::string ThreadSnapshotLinux::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_name_;
-}
-
 int ThreadSnapshotLinux::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return 0;
diff --git a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
index 4d1f7a7..40cd7e7 100644
--- a/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
+++ b/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
@@ -57,7 +57,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -81,7 +80,6 @@
   CPUContext context_;
   MemorySnapshotGeneric stack_;
   LinuxVMAddress thread_specific_data_address_;
-  std::string thread_name_;
   pid_t thread_id_;
   int priority_;
   InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc
index 5f9f8b7b..9b2a235 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.cc
@@ -75,7 +75,6 @@
     : thread_context(),
       float_context(),
       debug_context(),
-      name(),
       id(0),
       stack_region_address(0),
       stack_region_size(0),
@@ -366,20 +365,6 @@
       thread.thread_specific_data_address = identifier_info.thread_handle;
     }
 
-    thread_extended_info extended_info;
-    count = THREAD_EXTENDED_INFO_COUNT;
-    kr = thread_info(thread.port,
-                     THREAD_EXTENDED_INFO,
-                     reinterpret_cast<thread_info_t>(&extended_info),
-                     &count);
-    if (kr != KERN_SUCCESS) {
-      MACH_LOG(WARNING, kr) << "thread_info(THREAD_EXTENDED_INFO)";
-    } else {
-      thread.name.assign(
-          extended_info.pth_name,
-          strnlen(extended_info.pth_name, sizeof(extended_info.pth_name)));
-    }
-
     thread_precedence_policy precedence;
     count = THREAD_PRECEDENCE_POLICY_COUNT;
     boolean_t get_default = FALSE;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h
index 4f7792f..85cfe7c 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac.h
@@ -75,7 +75,6 @@
     ThreadContext thread_context;
     FloatContext float_context;
     DebugContext debug_context;
-    std::string name;
     uint64_t id;
     mach_vm_address_t stack_region_address;
     mach_vm_size_t stack_region_size;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
index 8d1ba2a..d5104e8 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
@@ -42,7 +42,6 @@
 #include "test/mac/dyld.h"
 #include "test/mac/mach_errors.h"
 #include "test/mac/mach_multiprocess.h"
-#include "test/scoped_set_thread_name.h"
 #include "util/file/file_io.h"
 #include "util/mac/mac_util.h"
 #include "util/mach/mach_extensions.h"
@@ -140,9 +139,6 @@
 }
 
 TEST(ProcessReaderMac, SelfOneThread) {
-  const ScopedSetThreadName scoped_set_thread_name(
-      "ProcessReaderMac/SelfOneThread");
-
   ProcessReaderMac process_reader;
   ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
 
@@ -155,7 +151,6 @@
   ASSERT_GE(threads.size(), 1u);
 
   EXPECT_EQ(threads[0].id, PthreadToThreadID(pthread_self()));
-  EXPECT_EQ(threads[0].name, "ProcessReaderMac/SelfOneThread");
 
   thread_t thread_self = MachThreadSelf();
   EXPECT_EQ(threads[0].port, thread_self);
@@ -168,11 +163,9 @@
   struct ThreadExpectation {
     mach_vm_address_t stack_address;
     int suspend_count;
-    std::string thread_name;
   };
 
-  TestThreadPool(const std::string& thread_name_prefix)
-      : thread_infos_(), thread_name_prefix_(thread_name_prefix) {}
+  TestThreadPool() : thread_infos_() {}
 
   TestThreadPool(const TestThreadPool&) = delete;
   TestThreadPool& operator=(const TestThreadPool&) = delete;
@@ -206,10 +199,7 @@
     ASSERT_TRUE(thread_infos_.empty());
 
     for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
-      std::string thread_name = base::StringPrintf(
-          "%s-%zu", thread_name_prefix_.c_str(), thread_index);
-      thread_infos_.push_back(
-          std::make_unique<ThreadInfo>(std::move(thread_name)));
+      thread_infos_.push_back(std::make_unique<ThreadInfo>());
       ThreadInfo* thread_info = thread_infos_.back().get();
 
       int rv = pthread_create(
@@ -245,20 +235,18 @@
     const auto& thread_info = thread_infos_[thread_index];
     expectation->stack_address = thread_info->stack_address;
     expectation->suspend_count = thread_info->suspend_count;
-    expectation->thread_name = thread_info->thread_name;
 
     return PthreadToThreadID(thread_info->pthread);
   }
 
  private:
   struct ThreadInfo {
-    ThreadInfo(const std::string& thread_name)
+    ThreadInfo()
         : pthread(nullptr),
           stack_address(0),
           ready_semaphore(0),
           exit_semaphore(0),
-          suspend_count(0),
-          thread_name(thread_name) {}
+          suspend_count(0) {}
 
     ~ThreadInfo() {}
 
@@ -282,14 +270,10 @@
 
     // The thread’s suspend count.
     int suspend_count;
-
-    // The thread's name.
-    const std::string thread_name;
   };
 
   static void* ThreadMain(void* argument) {
     ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);
-    const ScopedSetThreadName scoped_set_thread_name(thread_info->thread_name);
 
     thread_info->stack_address =
         FromPointerCast<mach_vm_address_t>(&thread_info);
@@ -309,9 +293,6 @@
   // This is a vector of pointers because the address of a ThreadInfo object is
   // passed to each thread’s ThreadMain(), so they cannot move around in memory.
   std::vector<std::unique_ptr<ThreadInfo>> thread_infos_;
-
-  // Prefix to use for each thread's name, suffixed with "-$threadindex".
-  const std::string thread_name_prefix_;
 };
 
 using ThreadMap = std::map<uint64_t, TestThreadPool::ThreadExpectation>;
@@ -347,7 +328,6 @@
       EXPECT_LT(iterator->second.stack_address, thread_stack_region_end);
 
       EXPECT_EQ(thread.suspend_count, iterator->second.suspend_count);
-      EXPECT_EQ(thread.name, iterator->second.thread_name);
 
       // Remove the thread from the expectation map since it’s already been
       // found. This makes it easy to check for duplicate thread IDs, and makes
@@ -395,7 +375,7 @@
   ProcessReaderMac process_reader;
   ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
 
-  TestThreadPool thread_pool("SelfSeveralThreads");
+  TestThreadPool thread_pool;
   constexpr size_t kChildThreads = 16;
   ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads));
 
@@ -413,8 +393,6 @@
     // There can’t be any duplicate thread IDs.
     EXPECT_EQ(thread_map.count(thread_id), 0u);
 
-    expectation.thread_name =
-        base::StringPrintf("SelfSeveralThreads-%zu", thread_index);
     thread_map[thread_id] = expectation;
   }
 
@@ -454,11 +432,8 @@
 
 class ProcessReaderThreadedChild final : public MachMultiprocess {
  public:
-  explicit ProcessReaderThreadedChild(const std::string thread_name_prefix,
-                                      size_t thread_count)
-      : MachMultiprocess(),
-        thread_name_prefix_(thread_name_prefix),
-        thread_count_(thread_count) {}
+  explicit ProcessReaderThreadedChild(size_t thread_count)
+      : MachMultiprocess(), thread_count_(thread_count) {}
 
   ProcessReaderThreadedChild(const ProcessReaderThreadedChild&) = delete;
   ProcessReaderThreadedChild& operator=(const ProcessReaderThreadedChild&) =
@@ -489,15 +464,6 @@
       CheckedReadFileExactly(read_handle,
                              &expectation.suspend_count,
                              sizeof(expectation.suspend_count));
-      std::string::size_type expected_thread_name_length;
-      CheckedReadFileExactly(read_handle,
-                             &expected_thread_name_length,
-                             sizeof(expected_thread_name_length));
-      std::string expected_thread_name(expected_thread_name_length, '\0');
-      CheckedReadFileExactly(read_handle,
-                             expected_thread_name.data(),
-                             expected_thread_name_length);
-      expectation.thread_name = expected_thread_name;
 
       // There can’t be any duplicate thread IDs.
       EXPECT_EQ(thread_map.count(thread_id), 0u);
@@ -514,13 +480,9 @@
   }
 
   void MachMultiprocessChild() override {
-    TestThreadPool thread_pool(thread_name_prefix_);
+    TestThreadPool thread_pool;
     ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_));
 
-    const std::string current_thread_name(base::StringPrintf(
-        "%s-MachMultiprocessChild", thread_name_prefix_.c_str()));
-    const ScopedSetThreadName scoped_set_thread_name(current_thread_name);
-
     FileHandle write_handle = WritePipeHandle();
 
     // This thread isn’t part of the thread pool, but the parent will be able
@@ -539,13 +501,6 @@
     CheckedWriteFile(write_handle,
                      &expectation.suspend_count,
                      sizeof(expectation.suspend_count));
-    const std::string::size_type current_thread_name_length =
-        current_thread_name.length();
-    CheckedWriteFile(write_handle,
-                     &current_thread_name_length,
-                     sizeof(current_thread_name_length));
-    CheckedWriteFile(
-        write_handle, current_thread_name.data(), current_thread_name_length);
 
     // Write an entry for everything in the thread pool.
     for (size_t thread_index = 0; thread_index < thread_count_;
@@ -559,16 +514,6 @@
       CheckedWriteFile(write_handle,
                        &expectation.suspend_count,
                        sizeof(expectation.suspend_count));
-      const std::string thread_pool_thread_name = base::StringPrintf(
-          "%s-%zu", thread_name_prefix_.c_str(), thread_index);
-      const std::string::size_type thread_pool_thread_name_length =
-          thread_pool_thread_name.length();
-      CheckedWriteFile(write_handle,
-                       &thread_pool_thread_name_length,
-                       sizeof(thread_pool_thread_name_length));
-      CheckedWriteFile(write_handle,
-                       thread_pool_thread_name.data(),
-                       thread_pool_thread_name_length);
     }
 
     // Wait for the parent to signal that it’s OK to exit by closing its end of
@@ -576,7 +521,6 @@
     CheckedReadFileAtEOF(ReadPipeHandle());
   }
 
-  const std::string thread_name_prefix_;
   size_t thread_count_;
 };
 
@@ -584,16 +528,14 @@
 TEST(ProcessReaderMac, DISABLED_ChildOneThread) {
   // The main thread plus zero child threads equals one thread.
   constexpr size_t kChildThreads = 0;
-  ProcessReaderThreadedChild process_reader_threaded_child("ChildOneThread",
-                                                           kChildThreads);
+  ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
   process_reader_threaded_child.Run();
 }
 
 // TODO(crbug.com/1319307): Test is failing on Mac. Re-enable it.
 TEST(ProcessReaderMac, DISABLED_ChildSeveralThreads) {
   constexpr size_t kChildThreads = 64;
-  ProcessReaderThreadedChild process_reader_threaded_child(
-      "ChildSeveralThreads", kChildThreads);
+  ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads);
   process_reader_threaded_child.Run();
 }
 
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
index 485ffc2..d261f194 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.cc
@@ -26,13 +26,13 @@
       context_union_(),
       context_(),
       stack_(),
-      thread_name_(),
       thread_id_(0),
       thread_specific_data_address_(0),
       thread_(MACH_PORT_NULL),
       suspend_count_(0),
       priority_(0),
-      initialized_() {}
+      initialized_() {
+}
 
 ThreadSnapshotMac::~ThreadSnapshotMac() {
 }
@@ -44,7 +44,6 @@
 
   thread_ = process_reader_thread.port;
   thread_id_ = process_reader_thread.id;
-  thread_name_ = process_reader_thread.name;
   suspend_count_ = process_reader_thread.suspend_count;
   priority_ = process_reader_thread.priority;
   thread_specific_data_address_ =
@@ -109,11 +108,6 @@
   return thread_id_;
 }
 
-std::string ThreadSnapshotMac::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_name_;
-}
-
 int ThreadSnapshotMac::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return suspend_count_;
diff --git a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
index ebdb3ae..4d9cb3167 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
+++ b/third_party/crashpad/crashpad/snapshot/mac/thread_snapshot_mac.h
@@ -18,8 +18,6 @@
 #include <mach/mach.h>
 #include <stdint.h>
 
-#include <string>
-
 #include "build/build_config.h"
 #include "snapshot/cpu_context.h"
 #include "snapshot/mac/process_reader_mac.h"
@@ -62,7 +60,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -81,7 +78,6 @@
   } context_union_;
   CPUContext context_;
   MemorySnapshotGeneric stack_;
-  std::string thread_name_;
   uint64_t thread_id_;
   uint64_t thread_specific_data_address_;
   thread_t thread_;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
index 2894244..8c870ee 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
+++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.cc
@@ -23,7 +23,6 @@
 #include "minidump/minidump_extensions.h"
 #include "snapshot/memory_map_region_snapshot.h"
 #include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
-#include "snapshot/minidump/minidump_string_reader.h"
 #include "util/file/file_io.h"
 
 namespace crashpad {
@@ -577,16 +576,12 @@
     return false;
   }
 
-  if (!InitializeThreadNames()) {
-    return false;
-  }
-
   for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {
     const RVA thread_rva = stream_it->second->Rva + sizeof(thread_count) +
                            thread_index * sizeof(MINIDUMP_THREAD);
 
     auto thread = std::make_unique<internal::ThreadSnapshotMinidump>();
-    if (!thread->Initialize(file_reader_, thread_rva, arch_, thread_names_)) {
+    if (!thread->Initialize(file_reader_, thread_rva, arch_)) {
       return false;
     }
 
@@ -596,59 +591,6 @@
   return true;
 }
 
-bool ProcessSnapshotMinidump::InitializeThreadNames() {
-  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadNameList);
-  if (stream_it == stream_map_.end()) {
-    return true;
-  }
-
-  if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_NAME_LIST)) {
-    LOG(ERROR) << "thread_name_list size mismatch";
-    return false;
-  }
-
-  if (!file_reader_->SeekSet(stream_it->second->Rva)) {
-    return false;
-  }
-
-  uint32_t thread_name_count;
-  if (!file_reader_->ReadExactly(&thread_name_count,
-                                 sizeof(thread_name_count))) {
-    return false;
-  }
-
-  if (sizeof(MINIDUMP_THREAD_NAME_LIST) +
-          thread_name_count * sizeof(MINIDUMP_THREAD_NAME) !=
-      stream_it->second->DataSize) {
-    LOG(ERROR) << "thread_name_list size mismatch";
-    return false;
-  }
-
-  for (uint32_t thread_name_index = 0; thread_name_index < thread_name_count;
-       ++thread_name_index) {
-    const RVA thread_name_rva =
-        stream_it->second->Rva + sizeof(thread_name_count) +
-        thread_name_index * sizeof(MINIDUMP_THREAD_NAME);
-    if (!file_reader_->SeekSet(thread_name_rva)) {
-      return false;
-    }
-    MINIDUMP_THREAD_NAME minidump_thread_name;
-    if (!file_reader_->ReadExactly(&minidump_thread_name,
-                                   sizeof(minidump_thread_name))) {
-      return false;
-    }
-    std::string name;
-    if (!internal::ReadMinidumpUTF16String(
-            file_reader_, minidump_thread_name.RvaOfThreadName, &name)) {
-      return false;
-    }
-
-    thread_names_.emplace(minidump_thread_name.ThreadId, std::move(name));
-  }
-
-  return true;
-}
-
 bool ProcessSnapshotMinidump::InitializeSystemSnapshot() {
   const auto& stream_it = stream_map_.find(kMinidumpStreamTypeSystemInfo);
   if (stream_it == stream_map_.end()) {
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
index 351fc4e..18fbc7c 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
+++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h
@@ -112,10 +112,6 @@
   // Initialize().
   bool InitializeThreads();
 
-  // Initializes data carried in a MINIDUMP_THREAD_NAME_LIST stream on behalf of
-  // Initialize().
-  bool InitializeThreadNames();
-
   // Initializes data carried in a MINIDUMP_MEMORY_INFO_LIST stream on behalf of
   // Initialize().
   bool InitializeMemoryInfo();
@@ -151,7 +147,6 @@
   std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
   std::vector<std::unique_ptr<internal::ModuleSnapshotMinidump>> modules_;
   std::vector<std::unique_ptr<internal::ThreadSnapshotMinidump>> threads_;
-  std::map<uint32_t, std::string> thread_names_;
   std::vector<UnloadedModuleSnapshot> unloaded_modules_;
   std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotMinidump>>
       mem_regions_;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
index 7fb4388..ded561b 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -728,104 +728,6 @@
   }
 }
 
-TEST(ProcessSnapshotMinidump, ThreadsWithNames) {
-  StringFile string_file;
-
-  MINIDUMP_HEADER header = {};
-  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
-
-  constexpr uint32_t kMinidumpThreadCount = 4;
-  constexpr uint32_t kBaseThreadId = 42;
-
-  const std::string thread_names[kMinidumpThreadCount] = {
-      "ariadne",
-      "theseus",
-      "pasiphae",
-      "minos",
-  };
-
-  RVA64 thread_name_rva64s[kMinidumpThreadCount];
-  for (uint32_t i = 0; i < kMinidumpThreadCount; i++) {
-    thread_name_rva64s[i] = static_cast<RVA64>(string_file.SeekGet());
-    auto name16 = base::UTF8ToUTF16(thread_names[i]);
-    uint32_t size =
-        base::checked_cast<uint32_t>(sizeof(name16[0]) * name16.size());
-    EXPECT_TRUE(string_file.Write(&size, sizeof(size)));
-    EXPECT_TRUE(string_file.Write(&name16[0], size));
-  }
-
-  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};
-  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;
-  minidump_thread_list_directory.Location.DataSize =
-      sizeof(MINIDUMP_THREAD_LIST) +
-      kMinidumpThreadCount * sizeof(MINIDUMP_THREAD);
-  minidump_thread_list_directory.Location.Rva =
-      static_cast<RVA>(string_file.SeekGet());
-
-  // Fields in MINIDUMP_THREAD_LIST.
-  EXPECT_TRUE(
-      string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount)));
-  for (uint32_t minidump_thread_index = 0;
-       minidump_thread_index < kMinidumpThreadCount;
-       ++minidump_thread_index) {
-    MINIDUMP_THREAD minidump_thread = {};
-    minidump_thread.ThreadId = kBaseThreadId + minidump_thread_index;
-    EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));
-  }
-
-  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
-  EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory,
-                                sizeof(minidump_thread_list_directory)));
-
-  MINIDUMP_DIRECTORY minidump_thread_name_list_directory = {};
-  minidump_thread_name_list_directory.StreamType =
-      kMinidumpStreamTypeThreadNameList;
-  minidump_thread_name_list_directory.Location.DataSize =
-      sizeof(MINIDUMP_THREAD_NAME_LIST) +
-      kMinidumpThreadCount * sizeof(MINIDUMP_THREAD_NAME);
-  minidump_thread_name_list_directory.Location.Rva =
-      static_cast<RVA>(string_file.SeekGet());
-
-  // Fields in MINIDUMP_THREAD_NAME_LIST.
-  EXPECT_TRUE(
-      string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount)));
-  for (uint32_t minidump_thread_index = 0;
-       minidump_thread_index < kMinidumpThreadCount;
-       ++minidump_thread_index) {
-    MINIDUMP_THREAD_NAME minidump_thread_name = {0, 0};
-    minidump_thread_name.ThreadId = kBaseThreadId + minidump_thread_index;
-    minidump_thread_name.RvaOfThreadName =
-        thread_name_rva64s[minidump_thread_index];
-    EXPECT_TRUE(
-        string_file.Write(&minidump_thread_name, sizeof(minidump_thread_name)));
-  }
-
-  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
-  ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,
-                                sizeof(minidump_thread_list_directory)));
-  ASSERT_TRUE(string_file.Write(&minidump_thread_name_list_directory,
-                                sizeof(minidump_thread_name_list_directory)));
-
-  header.Signature = MINIDUMP_SIGNATURE;
-  header.Version = MINIDUMP_VERSION;
-  header.NumberOfStreams = 2;
-  EXPECT_TRUE(string_file.SeekSet(0));
-  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
-
-  ProcessSnapshotMinidump process_snapshot;
-  EXPECT_TRUE(process_snapshot.Initialize(&string_file));
-
-  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();
-  ASSERT_EQ(threads.size(), kMinidumpThreadCount);
-
-  size_t idx = 0;
-  for (const auto& thread : threads) {
-    EXPECT_EQ(thread->ThreadID(), kBaseThreadId + idx);
-    EXPECT_EQ(thread->ThreadName(), thread_names[idx]);
-    idx++;
-  }
-}
-
 TEST(ProcessSnapshotMinidump, System) {
   const char* cpu_info = "GenuineIntel";
   const uint32_t* cpu_info_bytes = reinterpret_cast<const uint32_t*>(cpu_info);
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
index 26aabd7..f8f3673 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
+++ b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.cc
@@ -26,18 +26,15 @@
 ThreadSnapshotMinidump::ThreadSnapshotMinidump()
     : ThreadSnapshot(),
       minidump_thread_(),
-      thread_name_(),
       context_(),
       stack_(),
       initialized_() {}
 
 ThreadSnapshotMinidump::~ThreadSnapshotMinidump() {}
 
-bool ThreadSnapshotMinidump::Initialize(
-    FileReaderInterface* file_reader,
-    RVA minidump_thread_rva,
-    CPUArchitecture arch,
-    const std::map<uint32_t, std::string>& thread_names) {
+bool ThreadSnapshotMinidump::Initialize(FileReaderInterface* file_reader,
+                                        RVA minidump_thread_rva,
+                                        CPUArchitecture arch) {
   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
   std::vector<unsigned char> minidump_context;
 
@@ -70,10 +67,6 @@
   if (!stack_.Initialize(file_reader, stack_info_location)) {
     return false;
   }
-  const auto thread_name_iter = thread_names.find(minidump_thread_.ThreadId);
-  if (thread_name_iter != thread_names.end()) {
-    thread_name_ = thread_name_iter->second;
-  }
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
   return true;
@@ -84,11 +77,6 @@
   return minidump_thread_.ThreadId;
 }
 
-std::string ThreadSnapshotMinidump::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_name_;
-}
-
 int ThreadSnapshotMinidump::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return minidump_thread_.SuspendCount;
diff --git a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h
index b0ef424..7efb18d 100644
--- a/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h
+++ b/third_party/crashpad/crashpad/snapshot/minidump/thread_snapshot_minidump.h
@@ -17,8 +17,6 @@
 
 #include <windows.h>
 
-#include <map>
-
 #include "minidump/minidump_extensions.h"
 #include "snapshot/cpu_context.h"
 #include "snapshot/minidump/memory_snapshot_minidump.h"
@@ -48,20 +46,16 @@
   //!     the thread’s MINIDUMP_THREAD structure is located.
   //! \param[in] arch The architecture of the system this thread is running on.
   //!     Used to decode CPU Context.
-  //! \param[in] thread_names Map from thread ID to thread name previously read
-  //!     from the minidump's MINIDUMP_THREAD_NAME_LIST.
   //!
   //! \return `true` if the snapshot could be created, `false` otherwise with
   //!     an appropriate message logged.
   bool Initialize(FileReaderInterface* file_reader,
                   RVA minidump_thread_rva,
-                  CPUArchitecture arch,
-                  const std::map<uint32_t, std::string>& thread_names);
+                  CPUArchitecture arch);
 
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -77,7 +71,6 @@
   bool InitializeContext(const std::vector<unsigned char>& minidump_context);
 
   MINIDUMP_THREAD minidump_thread_;
-  std::string thread_name_;
   MinidumpContextConverter context_;
   MemorySnapshotMinidump stack_;
   InitializationStateDcheck initialized_;
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc
index 4a1fb72..186776e 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc
@@ -39,10 +39,6 @@
   return snapshot_->ThreadID();
 }
 
-std::string ThreadSnapshotSanitized::ThreadName() const {
-  return snapshot_->ThreadName();
-}
-
 int ThreadSnapshotSanitized::SuspendCount() const {
   return snapshot_->SuspendCount();
 }
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h
index b520579..dca0a0e 100644
--- a/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h
@@ -17,8 +17,6 @@
 
 #include "snapshot/thread_snapshot.h"
 
-#include <string>
-
 #include "snapshot/sanitized/memory_snapshot_sanitized.h"
 #include "util/misc/range_set.h"
 
@@ -46,7 +44,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc
index 3e73ecd..ed6d9fd 100644
--- a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc
+++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.cc
@@ -43,10 +43,6 @@
   return thread_id_;
 }
 
-std::string TestThreadSnapshot::ThreadName() const {
-  return thread_name_;
-}
-
 int TestThreadSnapshot::SuspendCount() const {
   return suspend_count_;
 }
diff --git a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h
index a1cef21..f865bdfe 100644
--- a/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h
+++ b/third_party/crashpad/crashpad/snapshot/test/test_thread_snapshot.h
@@ -18,7 +18,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string>
 #include <utility>
 #include <vector>
 
@@ -64,9 +63,6 @@
   }
 
   void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; }
-  void SetThreadName(const std::string& thread_name) {
-    thread_name_ = thread_name;
-  }
   void SetSuspendCount(int suspend_count) { suspend_count_ = suspend_count; }
   void SetPriority(int priority) { priority_ = priority; }
   void SetThreadSpecificDataAddress(uint64_t thread_specific_data_address) {
@@ -87,7 +83,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
@@ -101,7 +96,6 @@
   CPUContext context_;
   std::unique_ptr<MemorySnapshot> stack_;
   uint64_t thread_id_;
-  std::string thread_name_;
   int suspend_count_;
   int priority_;
   uint64_t thread_specific_data_address_;
diff --git a/third_party/crashpad/crashpad/snapshot/thread_snapshot.h b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
index ade3ee6..4d73257 100644
--- a/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
+++ b/third_party/crashpad/crashpad/snapshot/thread_snapshot.h
@@ -17,7 +17,6 @@
 
 #include <stdint.h>
 
-#include <string>
 #include <vector>
 
 namespace crashpad {
@@ -52,9 +51,6 @@
   //! unique system-wide.
   virtual uint64_t ThreadID() const = 0;
 
-  //! \brief Returns the thread's name.
-  virtual std::string ThreadName() const = 0;
-
   //! \brief Returns the thread’s suspend count.
   //!
   //! A suspend count of `0` denotes a schedulable (not suspended) thread.
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
index 45a932c0..2307380 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.cc
@@ -21,16 +21,13 @@
 
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
-#include "base/strings/utf_string_conversions.h"
 #include "snapshot/win/cpu_context_win.h"
 #include "util/misc/capture_context.h"
 #include "util/misc/time.h"
-#include "util/win/get_function.h"
 #include "util/win/nt_internals.h"
 #include "util/win/ntstatus_logging.h"
 #include "util/win/process_structs.h"
 #include "util/win/scoped_handle.h"
-#include "util/win/scoped_local_alloc.h"
 
 namespace crashpad {
 
@@ -235,9 +232,12 @@
 bool ProcessReaderWin::ThreadContext::InitializeXState(
     HANDLE thread_handle,
     ULONG64 XStateCompactionMask) {
-  // InitializeContext2 needs Windows 10 build 20348.
-  static const auto initialize_context_2 =
-      GET_FUNCTION(L"kernel32.dll", ::InitializeContext2);
+  static auto initialize_context_2 = []() {
+    // InitializeContext2 needs Windows 10 build 20348.
+    HINSTANCE kernel32 = GetModuleHandle(L"Kernel32.dll");
+    return reinterpret_cast<decltype(InitializeContext2)*>(
+        GetProcAddress(kernel32, "InitializeContext2"));
+  }();
   if (!initialize_context_2)
     return false;
   // We want CET_U xstate to get the ssp, only possible when supported.
@@ -276,7 +276,6 @@
 
 ProcessReaderWin::Thread::Thread()
     : context(),
-      name(),
       id(0),
       teb_address(0),
       teb_size(0),
@@ -461,21 +460,6 @@
         thread.stack_region_size = base - limit;
       }
     }
-    // On Windows 10 build 1607 and later, read the thread name.
-    static const auto get_thread_description =
-        GET_FUNCTION(L"kernel32.dll", ::GetThreadDescription);
-    if (get_thread_description) {
-      wchar_t* thread_description;
-      HRESULT hr =
-          get_thread_description(thread_handle.get(), &thread_description);
-      if (SUCCEEDED(hr)) {
-        ScopedLocalAlloc thread_description_owner(thread_description);
-        thread.name = base::WideToUTF8(thread_description);
-      } else {
-        LOG(WARNING) << "GetThreadDescription: "
-                     << logging::SystemErrorCodeToString(hr);
-      }
-    }
     threads_.push_back(thread);
   }
 }
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
index 90ff1cb..5987c00 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
+++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win.h
@@ -18,7 +18,6 @@
 #include <windows.h>
 #include <sys/time.h>
 
-#include <string>
 #include <vector>
 
 #include "build/build_config.h"
@@ -78,7 +77,6 @@
     ~Thread() {}
 
     ThreadContext context;
-    std::string name;
     uint64_t id;
     WinVMAddress teb_address;
     WinVMSize teb_size;
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
index 98cb11b..15a6e2b 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_reader_win_test.cc
@@ -17,18 +17,9 @@
 #include <windows.h>
 #include <string.h>
 
-#include <algorithm>
-#include <array>
 #include <iterator>
-#include <set>
-#include <string>
 
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "test/scoped_set_thread_name.h"
 #include "test/win/win_multiprocess.h"
 #include "util/misc/from_pointer_cast.h"
 #include "util/synchronization/semaphore.h"
@@ -40,8 +31,6 @@
 namespace test {
 namespace {
 
-using ::testing::IsSupersetOf;
-
 TEST(ProcessReaderWin, SelfBasic) {
   ProcessReaderWin process_reader;
   ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
@@ -109,7 +98,6 @@
 }
 
 TEST(ProcessReaderWin, SelfOneThread) {
-  const ScopedSetThreadName scoped_set_thread_name("SelfBasic");
   ProcessReaderWin process_reader;
   ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
                                         ProcessSuspensionState::kRunning));
@@ -123,7 +111,6 @@
   ASSERT_GE(threads.size(), 1u);
 
   EXPECT_EQ(threads[0].id, GetCurrentThreadId());
-  EXPECT_EQ(threads[0].name, "SelfBasic");
   EXPECT_NE(ProgramCounterFromCONTEXT(threads[0].context.context<CONTEXT>()),
             nullptr);
   EXPECT_EQ(threads[0].suspend_count, 0u);
@@ -145,21 +132,18 @@
 
   class SleepingThread : public Thread {
    public:
-    explicit SleepingThread(const std::string& thread_name)
-        : done_(nullptr), thread_name_(thread_name) {}
+    SleepingThread() : done_(nullptr) {}
 
     void SetHandle(Semaphore* done) {
       done_= done;
     }
 
     void ThreadMain() override {
-      const ScopedSetThreadName scoped_set_thread_name(thread_name_);
       done_->Wait();
     }
 
    private:
     Semaphore* done_;
-    const std::string thread_name_;
   };
 
   void WinMultiprocessParent() override {
@@ -174,28 +158,8 @@
 
       const auto& threads = process_reader.Threads();
       ASSERT_GE(threads.size(), kCreatedThreads + 1);
-      EXPECT_EQ(threads[0].name, "WinMultiprocessChild-Main");
-
-      const std::set<std::string> expected_thread_names = {
-          "WinMultiprocessChild-1",
-          "WinMultiprocessChild-2",
-          "WinMultiprocessChild-3",
-      };
-      // Windows can create threads besides the ones created in
-      // WinMultiprocessChild(), so keep track of the (non-main) thread names
-      // and make sure all the expected names are present.
-      std::set<std::string> thread_names;
-      for (size_t i = 1; i < threads.size(); i++) {
-        if (!threads[i].name.empty()) {
-          thread_names.emplace(threads[i].name);
-        }
-      }
-
-      EXPECT_THAT(thread_names, IsSupersetOf(expected_thread_names));
-
-      for (const auto& thread : threads) {
+      for (const auto& thread : threads)
         EXPECT_EQ(thread.suspend_count, 0u);
-      }
     }
 
     {
@@ -209,23 +173,15 @@
       // suspended.
       const auto& threads = process_reader.Threads();
       ASSERT_GE(threads.size(), kCreatedThreads + 1);
-      for (const auto& thread : threads) {
+      for (const auto& thread : threads)
         EXPECT_EQ(thread.suspend_count, 0u);
-      }
     }
   }
 
   void WinMultiprocessChild() override {
-    const ScopedSetThreadName scoped_set_thread_name(
-        "WinMultiprocessChild-Main");
-
     // Create three dummy threads so we can confirm we read successfully read
     // more than just the main thread.
-    std::array<SleepingThread, kCreatedThreads> threads = {
-        SleepingThread(std::string("WinMultiprocessChild-1")),
-        SleepingThread(std::string("WinMultiprocessChild-2")),
-        SleepingThread(std::string("WinMultiprocessChild-3")),
-    };
+    SleepingThread threads[kCreatedThreads];
     Semaphore done(0);
     for (auto& thread : threads)
       thread.SetHandle(&done);
diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
index 844986db..f1a20b5 100644
--- a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win.cc
@@ -45,7 +45,8 @@
       annotations_simple_map_(),
       snapshot_time_(),
       options_(),
-      initialized_() {}
+      initialized_() {
+}
 
 ProcessSnapshotWin::~ProcessSnapshotWin() {
 }
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc
index 222b2ab..2c5569f8 100644
--- a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc
+++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.cc
@@ -165,11 +165,6 @@
   return thread_.id;
 }
 
-std::string ThreadSnapshotWin::ThreadName() const {
-  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return thread_.name;
-}
-
 int ThreadSnapshotWin::SuspendCount() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
   return thread_.suspend_count;
diff --git a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h
index af18a14..b9fafaa 100644
--- a/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h
+++ b/third_party/crashpad/crashpad/snapshot/win/thread_snapshot_win.h
@@ -68,7 +68,6 @@
   const CPUContext* Context() const override;
   const MemorySnapshot* Stack() const override;
   uint64_t ThreadID() const override;
-  std::string ThreadName() const override;
   int SuspendCount() const override;
   int Priority() const override;
   uint64_t ThreadSpecificDataAddress() const override;
diff --git a/third_party/crashpad/crashpad/test/BUILD.gn b/third_party/crashpad/crashpad/test/BUILD.gn
index fef0fc7..c2ad5b92 100644
--- a/third_party/crashpad/crashpad/test/BUILD.gn
+++ b/third_party/crashpad/crashpad/test/BUILD.gn
@@ -37,7 +37,6 @@
     "scoped_guarded_page.h",
     "scoped_module_handle.cc",
     "scoped_module_handle.h",
-    "scoped_set_thread_name.h",
     "scoped_temp_dir.cc",
     "scoped_temp_dir.h",
     "test_paths.cc",
@@ -58,14 +57,6 @@
     }
   }
 
-  # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no
-  # longer treated as a posix platform.
-  if (crashpad_is_posix && !crashpad_is_fuchsia) {
-    sources += [
-      "scoped_set_thread_name_posix.cc",
-    ]
-  }
-
   if (crashpad_is_mac || crashpad_is_ios) {
     sources += [
       "mac/mach_errors.cc",
@@ -105,7 +96,6 @@
     sources += [
       "multiprocess_exec_win.cc",
       "scoped_guarded_page_win.cc",
-      "scoped_set_thread_name_win.cc",
       "scoped_temp_dir_win.cc",
       "win/child_launcher.cc",
       "win/child_launcher.h",
@@ -119,10 +109,7 @@
   }
 
   if (crashpad_is_fuchsia) {
-    sources += [
-      "multiprocess_exec_fuchsia.cc",
-      "scoped_set_thread_name_fuchsia.cc",
-    ]
+    sources += [ "multiprocess_exec_fuchsia.cc" ]
   }
 
   public_configs = [ "..:crashpad_config" ]
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name.h b/third_party/crashpad/crashpad/test/scoped_set_thread_name.h
deleted file mode 100644
index 69998c0..0000000
--- a/third_party/crashpad/crashpad/test/scoped_set_thread_name.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2022 The Crashpad 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.
-
-#ifndef CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_
-#define CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_
-
-#include <string>
-
-#include "build/build_config.h"
-
-namespace crashpad {
-namespace test {
-
-//! Sets the name of the current thread for the lifetime of this object.
-class ScopedSetThreadName final {
- public:
-  explicit ScopedSetThreadName(const std::string& new_thread_name);
-
-  ScopedSetThreadName(const ScopedSetThreadName&) = delete;
-  ScopedSetThreadName& operator=(const ScopedSetThreadName&) = delete;
-
-  ~ScopedSetThreadName();
-
- private:
-#if BUILDFLAG(IS_WIN)
-  const std::wstring original_name_;
-#else
-  const std::string original_name_;
-#endif
-};
-
-}  // namespace test
-}  // namespace crashpad
-
-#endif  // CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc
deleted file mode 100644
index 7b672d15..0000000
--- a/third_party/crashpad/crashpad/test/scoped_set_thread_name_fuchsia.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2022 The Crashpad 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 "test/scoped_set_thread_name.h"
-
-#include <string>
-
-#include <lib/zx/thread.h>
-#include <zircon/syscalls/object.h>
-#include <zircon/types.h>
-
-#include "base/check_op.h"
-#include "base/fuchsia/fuchsia_logging.h"
-
-namespace crashpad {
-namespace test {
-
-namespace {
-
-std::string GetCurrentThreadName() {
-  std::string result(ZX_MAX_NAME_LEN, '\0');
-  const zx_status_t status = zx::thread::self()->get_property(
-      ZX_PROP_NAME, result.data(), result.length());
-  ZX_CHECK(status == ZX_OK, status) << "get_property(ZX_PROP_NAME)";
-  const auto result_nul_idx = result.find('\0');
-  CHECK_NE(result_nul_idx, std::string::npos)
-      << "get_property() did not NUL terminate";
-  result.resize(result_nul_idx);
-  return result;
-}
-
-}  // namespace
-
-ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)
-    : original_name_(GetCurrentThreadName()) {
-  // Fuchsia silently truncates the thread name if it's too long.
-  CHECK_LT(new_thread_name.length(), ZX_MAX_NAME_LEN);
-  const zx_status_t status = zx::thread::self()->set_property(
-      ZX_PROP_NAME, new_thread_name.c_str(), new_thread_name.length());
-  ZX_CHECK(status == ZX_OK, status) << "set_property(ZX_PROP_NAME)";
-}
-
-ScopedSetThreadName::~ScopedSetThreadName() {
-  const zx_status_t status = zx::thread::self()->set_property(
-      ZX_PROP_NAME, original_name_.c_str(), original_name_.length());
-  ZX_CHECK(status == ZX_OK, status) << "set_property(ZX_PROP_NAME)";
-}
-
-}  // namespace test
-}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc
deleted file mode 100644
index 92c61bb..0000000
--- a/third_party/crashpad/crashpad/test/scoped_set_thread_name_posix.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2022 The Crashpad 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 "test/scoped_set_thread_name.h"
-
-#include <errno.h>
-#include <pthread.h>
-
-#include <ostream>
-#include <string>
-
-#include "base/check.h"
-#include "base/check_op.h"
-#include "build/build_config.h"
-
-#if BUILDFLAG(IS_APPLE)
-#include <mach/thread_info.h>
-#elif BUILDFLAG(IS_ANDROID)
-#include <sys/prctl.h>
-#endif
-
-namespace crashpad {
-namespace test {
-
-namespace {
-
-#if BUILDFLAG(IS_APPLE)
-constexpr size_t kPthreadNameMaxLen = MAXTHREADNAMESIZE;
-#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
-// The kernel headers define this in linux/sched.h as TASK_COMM_LEN, but the
-// userspace copy of that header does not define it.
-constexpr size_t kPthreadNameMaxLen = 16;
-#else
-#error Port to your platform
-#endif
-
-void SetCurrentThreadName(const std::string& thread_name) {
-#if BUILDFLAG(IS_APPLE)
-  // Apple's pthread_setname_np() sets errno instead of returning it.
-  PCHECK(pthread_setname_np(thread_name.c_str()) == 0) << "pthread_setname_np";
-#elif BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24
-  // pthread_setname_np() requires Android API 24 or later.
-  CHECK_LT(thread_name.length(), kPthreadNameMaxLen);
-  PCHECK(prctl(PR_SET_NAME, thread_name.c_str()) == 0) << "prctl(PR_SET_NAME)";
-#else
-  PCHECK((errno = pthread_setname_np(pthread_self(), thread_name.c_str())) == 0)
-      << "pthread_setname_np";
-#endif
-}
-
-std::string GetCurrentThreadName() {
-  std::string result(kPthreadNameMaxLen, '\0');
-#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24
-  static constexpr char kGetThreadNameFunctionName[] = "prctl";
-  PCHECK(prctl(PR_GET_NAME, result.data()) == 0) << "prctl(PR_GET_NAME)";
-#else
-  static constexpr char kGetThreadNameFunctionName[] = "pthread_getname_np";
-  PCHECK((errno = pthread_getname_np(
-              pthread_self(), result.data(), result.length())) == 0)
-      << "pthread_getname_np";
-#endif
-  const auto result_nul_idx = result.find('\0');
-  CHECK(result_nul_idx != std::string::npos)
-      << kGetThreadNameFunctionName << " did not NUL terminate";
-  result.resize(result_nul_idx);
-  return result;
-}
-
-}  // namespace
-
-ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)
-    : original_name_(GetCurrentThreadName()) {
-  SetCurrentThreadName(new_thread_name);
-}
-
-ScopedSetThreadName::~ScopedSetThreadName() {
-  SetCurrentThreadName(original_name_);
-}
-
-}  // namespace test
-}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc b/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc
deleted file mode 100644
index 482d3c12..0000000
--- a/third_party/crashpad/crashpad/test/scoped_set_thread_name_win.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2022 The Crashpad 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 "test/scoped_set_thread_name.h"
-
-#include <windows.h>
-
-#include "base/check.h"
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "util/win/scoped_local_alloc.h"
-
-namespace crashpad {
-namespace test {
-
-namespace {
-
-std::wstring GetCurrentThreadName() {
-  wchar_t* thread_description;
-  HRESULT hr = GetThreadDescription(GetCurrentThread(), &thread_description);
-  CHECK(SUCCEEDED(hr)) << "GetThreadDescription: "
-                       << logging::SystemErrorCodeToString(hr);
-  ScopedLocalAlloc thread_description_owner(thread_description);
-  return std::wstring(thread_description);
-}
-
-}  // namespace
-
-ScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)
-    : original_name_(GetCurrentThreadName()) {
-  const std::wstring wnew_thread_name = base::UTF8ToWide(new_thread_name);
-  HRESULT hr =
-      SetThreadDescription(GetCurrentThread(), wnew_thread_name.c_str());
-  CHECK(SUCCEEDED(hr)) << "SetThreadDescription: "
-                       << logging::SystemErrorCodeToString(hr);
-}
-
-ScopedSetThreadName::~ScopedSetThreadName() {
-  HRESULT hr = SetThreadDescription(GetCurrentThread(), original_name_.c_str());
-  CHECK(SUCCEEDED(hr)) << "SetThreadDescription: "
-                       << logging::SystemErrorCodeToString(hr);
-}
-
-}  // namespace test
-}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h
index ea206430dd..5e36862 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h
+++ b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_format.h
@@ -99,7 +99,6 @@
     TD(kThreadContextMemoryRegions, 6011) \
     TD(kThreadContextMemoryRegionAddress, 6012) \
     TD(kThreadContextMemoryRegionData, 6013) \
-    TD(kThreadName, 6014) \
   TD(kMaxValue, 65535) \
 // clang-format on
 
diff --git a/third_party/crashpad/crashpad/util/posix/signals.cc b/third_party/crashpad/crashpad/util/posix/signals.cc
index 93874e5..f53ceb2 100644
--- a/third_party/crashpad/crashpad/util/posix/signals.cc
+++ b/third_party/crashpad/crashpad/util/posix/signals.cc
@@ -147,25 +147,6 @@
     PLOG(ERROR) << "sigaction " << sig;
     return false;
   }
-
-// Sanitizers can prevent the installation of signal handlers, but sigaction
-// does not report this as failure. Attempt to detect this by checking the
-// currently installed signal handler.
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
-    defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) ||    \
-    defined(UNDEFINED_SANITIZER)
-  struct sigaction installed_handler;
-  CHECK_EQ(sigaction(sig, nullptr, &installed_handler), 0);
-  // If the installed handler does not point to the just installed handler, then
-  // the allow_user_segv_handler sanitizer flag is (probably) disabled.
-  if (installed_handler.sa_sigaction != handler) {
-    LOG(WARNING)
-        << "sanitizers are preventing signal handler installation (sig " << sig
-        << ")";
-    return false;
-  }
-#endif
-
   return true;
 }
 
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 5127fac..35ca5ad 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -206,7 +206,6 @@
 src/webgpu/api/validation/encoding/createRenderBundleEncoder.spec.ts
 src/webgpu/api/validation/encoding/encoder_state.spec.ts
 src/webgpu/api/validation/encoding/render_bundle.spec.ts
-src/webgpu/api/validation/encoding/cmds/buffer_texture_copies.spec.ts
 src/webgpu/api/validation/encoding/cmds/clearBuffer.spec.ts
 src/webgpu/api/validation/encoding/cmds/compute_pass.spec.ts
 src/webgpu/api/validation/encoding/cmds/copyBufferToBuffer.spec.ts
@@ -231,6 +230,7 @@
 src/webgpu/api/validation/encoding/queries/resolveQuerySet.spec.ts
 src/webgpu/api/validation/image_copy/image_copy.ts
 src/webgpu/api/validation/image_copy/buffer_related.spec.ts
+src/webgpu/api/validation/image_copy/buffer_texture_copies.spec.ts
 src/webgpu/api/validation/image_copy/layout_related.spec.ts
 src/webgpu/api/validation/image_copy/texture_related.spec.ts
 src/webgpu/api/validation/initialization/requestDevice.spec.ts
diff --git a/tools/generate_stubs/generate_stubs.py b/tools/generate_stubs/generate_stubs.py
index 7f1be38..1b99c1bd 100755
--- a/tools/generate_stubs/generate_stubs.py
+++ b/tools/generate_stubs/generate_stubs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2012 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.
@@ -147,7 +147,6 @@
 # following named parameters:
 #   guard_name: The macro to use as the header guard.
 #   namespace: The namespace for the stub functions.
-#   logging_include: Header file where the logging function is defined.
 STUB_HEADER_PREAMBLE = """// This is generated file. Do not modify directly.
 
 #ifndef %(guard_name)s
@@ -466,7 +465,7 @@
   with |filter|."""
   popen = subprocess.Popen(args, stdout=subprocess.PIPE)
   out, _ = popen.communicate()
-  for line in out.splitlines():
+  for line in out.decode('utf-8').splitlines():
     if not filter or not line.startswith(filter):
       write_to.write(line + '\n')
   return popen.returncode
@@ -786,7 +785,6 @@
       namespace: The namespace these functions should be in.
       header_guard: The macro to use as our header guard.
       outfile: The output handle to populate.
-      logging_include: Header file where the logging function is defined.
     """
     outfile.write(STUB_HEADER_PREAMBLE % {
         'guard_name': header_guard,
diff --git a/tools/generate_stubs/generate_stubs_unittest.py b/tools/generate_stubs/generate_stubs_unittest.py
index fe4878e4..dbdeb67 100755
--- a/tools/generate_stubs/generate_stubs_unittest.py
+++ b/tools/generate_stubs/generate_stubs_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2012 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.
@@ -21,7 +21,7 @@
 
 import generate_stubs as gs
 import re
-import StringIO
+import io
 import sys
 import unittest
 
@@ -73,18 +73,18 @@
 
   def testParseSignatures_EmptyFile(self):
     # Empty file just generates empty signatures.
-    infile = StringIO.StringIO()
+    infile = io.StringIO()
     signatures = gs.ParseSignatures(infile)
     self.assertEqual(0, len(signatures))
 
   def testParseSignatures_SimpleSignatures(self):
     file_contents = '\n'.join([x[0] for x in SIMPLE_SIGNATURES])
-    infile = StringIO.StringIO(file_contents)
+    infile = io.StringIO(file_contents)
     signatures = gs.ParseSignatures(infile)
     self.assertEqual(len(SIMPLE_SIGNATURES), len(signatures))
 
     # We assume signatures are in order.
-    for i in xrange(len(SIMPLE_SIGNATURES)):
+    for i in range(len(SIMPLE_SIGNATURES)):
       self.assertEqual(SIMPLE_SIGNATURES[i][1], signatures[i],
                        msg='Expected %s\nActual %s\nFor %s' %
                        (SIMPLE_SIGNATURES[i][1],
@@ -93,12 +93,12 @@
 
   def testParseSignatures_TrickySignatures(self):
     file_contents = '\n'.join([x[0] for x in TRICKY_SIGNATURES])
-    infile = StringIO.StringIO(file_contents)
+    infile = io.StringIO(file_contents)
     signatures = gs.ParseSignatures(infile)
     self.assertEqual(len(TRICKY_SIGNATURES), len(signatures))
 
     # We assume signatures are in order.
-    for i in xrange(len(TRICKY_SIGNATURES)):
+    for i in range(len(TRICKY_SIGNATURES)):
       self.assertEqual(TRICKY_SIGNATURES[i][1], signatures[i],
                        msg='Expected %s\nActual %s\nFor %s' %
                        (TRICKY_SIGNATURES[i][1],
@@ -107,7 +107,7 @@
 
   def testParseSignatures_InvalidSignatures(self):
     for i in INVALID_SIGNATURES:
-      infile = StringIO.StringIO(i)
+      infile = io.StringIO(i)
       self.assertRaises(gs.BadSignatureError, gs.ParseSignatures, infile)
 
   def testParseSignatures_CommentsIgnored(self):
@@ -124,7 +124,7 @@
     my_sigs.append(SIMPLE_SIGNATURES[0][0])
 
     file_contents = '\n'.join(my_sigs)
-    infile = StringIO.StringIO(file_contents)
+    infile = io.StringIO(file_contents)
     signatures = gs.ParseSignatures(infile)
     self.assertEqual(5, len(signatures))
 
@@ -133,7 +133,7 @@
   def testWriteWindowsDefFile(self):
     module_name = 'my_module-1'
     signatures = [sig[1] for sig in SIMPLE_SIGNATURES]
-    outfile = StringIO.StringIO()
+    outfile = io.StringIO()
     gs.WriteWindowsDefFile(module_name, signatures, outfile)
     contents = outfile.getvalue()
 
@@ -149,18 +149,18 @@
                       msg='Expected match of "%s" in %s' % (pattern, contents))
 
   def testQuietRun(self):
-    output = StringIO.StringIO()
+    output = io.StringIO()
     gs.QuietRun([
-        sys.executable, '-c',
-        'from __future__ import print_function; print("line 1 and suffix\\nline 2")'
+        sys.executable, '-c', 'from __future__ import print_function; '
+        'print("line 1 and suffix\\nline 2")'
     ],
                 write_to=output)
     self.assertEqual('line 1 and suffix\nline 2\n', output.getvalue())
 
-    output = StringIO.StringIO()
+    output = io.StringIO()
     gs.QuietRun([
-        sys.executable, '-c',
-        'from __future__ import print_function; print("line 1 and suffix\\nline 2")'
+        sys.executable, '-c', 'from __future__ import print_function; '
+        'print("line 1 and suffix\\nline 2")'
     ],
                 filter='line 1',
                 write_to=output)
@@ -195,18 +195,22 @@
 
   def testStubFunctionPointer(self):
     self.assertEqual(
-        'static int (*foo_ptr)(int a) = NULL;',
+        'static int (*foo_ptr)(int a) = nullptr;',
         gs.PosixStubWriter.StubFunctionPointer(SIMPLE_SIGNATURES[0][1]))
 
   def testStubFunction(self):
     # Test for a signature with a return value and a parameter.
-    self.assertEqual("""extern int foo(int a) __attribute__((weak));
+    self.assertEqual(
+        """extern int foo(int a) __attribute__((weak));
+DISABLE_CFI_ICALL
 int  foo(int a) {
   return foo_ptr(a);
 }""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[0][1]))
 
     # Test for a signature with a void return value and no parameters.
-    self.assertEqual("""extern void waldo(void) __attribute__((weak));
+    self.assertEqual(
+        """extern void waldo(void) __attribute__((weak));
+DISABLE_CFI_ICALL
 void  waldo(void) {
   waldo_ptr();
 }""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[4][1]))
@@ -214,20 +218,24 @@
     # Test export macros.
     sig = _MakeSignature('int*', 'foo', ['bool b'])
     sig['export'] = 'TEST_EXPORT'
-    self.assertEqual("""extern int* foo(bool b) __attribute__((weak));
+    self.assertEqual(
+        """extern int* foo(bool b) __attribute__((weak));
+DISABLE_CFI_ICALL
 int* TEST_EXPORT foo(bool b) {
   return foo_ptr(b);
 }""", gs.PosixStubWriter.StubFunction(sig))
 
     # Test for a signature where an array is passed. It should be passed without
     # square brackets otherwise the compilation failure will occur..
-    self.assertEqual("""extern int ferda(char **argv[]) __attribute__((weak));
+    self.assertEqual(
+        """extern int ferda(char **argv[]) __attribute__((weak));
+DISABLE_CFI_ICALL
 int  ferda(char **argv[]) {
   return ferda_ptr(argv);
 }""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[6][1]))
 
   def testWriteImplemenationContents(self):
-    outfile = StringIO.StringIO()
+    outfile = io.StringIO()
     self.writer.WriteImplementationContents('my_namespace', outfile)
     contents = outfile.getvalue()
 
@@ -263,9 +271,9 @@
     module_names = ['oneModule', 'twoModule']
 
     # Make the header.
-    outfile = StringIO.StringIO()
+    outfile = io.StringIO()
     self.writer.WriteHeaderContents(module_names, 'my_namespace', 'GUARD_',
-                                    outfile, 'base/logging.h')
+                                    outfile)
     contents = outfile.getvalue()
 
     # Check for namespace and header guard.
@@ -298,7 +306,7 @@
     module_names = ['oneModule', 'twoModule']
 
     # Make the header.
-    outfile = StringIO.StringIO()
+    outfile = io.StringIO()
     self.writer.WriteUmbrellaInitializer(module_names, 'my_namespace', outfile,
                                          'VLOG(1)')
     contents = outfile.getvalue()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8bd314f..19671b16 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -33095,6 +33095,7 @@
   <int value="493" label="LOGIN_ON_EXTERNAL_LOGOUT_DONE"/>
   <int value="494" label="ACCESSIBILITY_PRIVATE_ON_PUMPKIN_INSTALLED"/>
   <int value="495" label="REMOTE_APPS_ON_REMOTE_APP_LAUNCHED"/>
+  <int value="496" label="INPUT_METHOD_PRIVATE_ON_CARET_BOUNDS_CHANGED"/>
 </enum>
 
 <enum name="ExtensionFileWriteResult">
@@ -58954,6 +58955,7 @@
   <int value="402630521"
       label="IPH_KeyboardAccessoryPaymentVirtualCard:enabled"/>
   <int value="403288255" label="enable-wheel-scroll-latching"/>
+  <int value="403554154" label="SafetyCheckWeakPasswords:enabled"/>
   <int value="405329388"
       label="FramebustingNeedsSameOriginOrUserGesture:enabled"/>
   <int value="406549604" label="NtpSafeBrowsingModule:enabled"/>
@@ -59406,6 +59408,7 @@
   <int value="700926106" label="OmniboxPedalsBatch3NonEnglish:enabled"/>
   <int value="701766325" label="PerNavigationMojoInterface:enabled"/>
   <int value="701795774" label="WebAppEnableUrlHandlers:enabled"/>
+  <int value="702060507" label="SafetyCheckWeakPasswords:disabled"/>
   <int value="702117246" label="OmniboxSiteSearchStarterPack:disabled"/>
   <int value="703122112" label="ShoppingList:enabled"/>
   <int value="703469777" label="AndroidNightModeTabReparenting:enabled"/>
@@ -59477,6 +59480,7 @@
   <int value="741760108" label="SidePanelPrototype:enabled"/>
   <int value="742083923" label="MimeHandlerViewInCrossProcessFrame:disabled"/>
   <int value="743714331" label="HelpAppLauncherSearch:enabled"/>
+  <int value="744342941" label="SafetyCheckChromeCleanerChild:enabled"/>
   <int value="745541471" label="PaintHolding:disabled"/>
   <int value="745783589" label="translate-force-trigger-on-english"/>
   <int value="745850072" label="FastCheckout:disabled"/>
@@ -59567,6 +59571,7 @@
   <int value="803982925" label="CellularForbidAttachApn:disabled"/>
   <int value="804551127" label="ArcKeyboardShortcutHelperIntegration:disabled"/>
   <int value="804958673" label="LensCameraAssistedSearch:enabled"/>
+  <int value="805882800" label="SafetyCheckChromeCleanerChild:disabled"/>
   <int value="806035639" label="EnableNeuralPalmDetectionFilter:disabled"/>
   <int value="806334184" label="AndroidSpellChecker:enabled"/>
   <int value="806783713" label="DesktopPWAsTabStripLinkCapturing:enabled"/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index ac60465..33979a9 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -434,6 +434,23 @@
   </summary>
 </histogram>
 
+<histogram name="TrustedWebActivity.ExtraCommandSuccess.{command}"
+    enum="BooleanSuccess" expires_after="2022-11-23">
+  <owner>mvanouwerkerk@google.com</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>
+    src/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OWNERS
+  </owner>
+  <summary>
+    Records whether the extra command {command} called from
+    TrustedWebActivityClient resulted in success or failure.
+  </summary>
+  <token key="command">
+    <variant name="checkNotificationPermission"/>
+    <variant name="getNotificationPermissionRequestPendingIntent"/>
+  </token>
+</histogram>
+
 <histogram name="TrustedWebActivity.LocationDelegationEnrolled" enum="Boolean"
     expires_after="2022-11-13">
   <owner>eirage@chromium.org</owner>
@@ -477,6 +494,21 @@
   </summary>
 </histogram>
 
+<histogram name="TrustedWebActivity.Notification.PermissionRequestResult"
+    enum="ContentSetting" expires_after="2022-12-04">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>
+    src/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OWNERS
+  </owner>
+  <summary>
+    The result of a TWA notification permission request for the purpose of
+    notification delegation. This is logged when the
+    NotificationPermissionUpdater receives the result from the
+    TrustedWebActivityClient.
+  </summary>
+</histogram>
+
 <histogram name="TrustedWebActivity.QualityEnforcementViolation"
     enum="TrustedWebActivityQualityEnforcementViolationType"
     expires_after="M104">
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 4912c002..2fa4021 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -1652,7 +1652,6 @@
 
 <histogram name="Enterprise.LoginApiCleanup.{CleanupHandler}.Success"
     enum="BooleanSuccess" expires_after="2022-12-31">
-  <owner>jityao@google.com</owner>
   <owner>hendrich@chromium.org</owner>
   <summary>ChromeOS only. Records if cleanup handler succeeded.</summary>
   <token key="CleanupHandler" variants="CleanupHandler"/>
@@ -1660,7 +1659,6 @@
 
 <histogram name="Enterprise.LoginApiCleanup.{CleanupHandler}.Timing" units="ms"
     expires_after="2022-12-31">
-  <owner>jityao@google.com</owner>
   <owner>hendrich@chromium.org</owner>
   <summary>
     ChromeOS only. Records the duration the cleanup handler took.
diff --git a/tools/metrics/histograms/metadata/na_cl/OWNERS b/tools/metrics/histograms/metadata/na_cl/OWNERS
index ab753e84..c1332c20 100644
--- a/tools/metrics/histograms/metadata/na_cl/OWNERS
+++ b/tools/metrics/histograms/metadata/na_cl/OWNERS
@@ -3,3 +3,4 @@
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
 dschuff@chromium.org
+fabiansommer@chromium.org
diff --git a/tools/metrics/histograms/metadata/na_cl/histograms.xml b/tools/metrics/histograms/metadata/na_cl/histograms.xml
index 744353f..262dee8 100644
--- a/tools/metrics/histograms/metadata/na_cl/histograms.xml
+++ b/tools/metrics/histograms/metadata/na_cl/histograms.xml
@@ -25,8 +25,7 @@
 <histogram name="NaCl.AppType" enum="NaClAppTypeEnum"
     expires_after="2022-12-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     Type of NaCl/PNaCl application/extension. Recorded at plugin creation time
     after the permission check.
@@ -34,20 +33,18 @@
 </histogram>
 
 <histogram name="NaCl.Client.Helper.InitState" enum="NaClHelperStatus"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     When the browser started, what happened with the NaCl helper process?
   </summary>
 </histogram>
 
 <histogram name="NaCl.Client.Helper.StateOnFork" enum="NaClHelperStatus"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     When a NaCl application process was created, what had happened with the NaCl
     helper process when the browser was started?
@@ -55,18 +52,16 @@
 </histogram>
 
 <histogram name="NaCl.Client.OSArch" enum="NaClOSArchEnum"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The OS/Architecture of a nexe that was loaded.</summary>
 </histogram>
 
 <histogram name="NaCl.HttpStatusCodeClass.Manifest.InstalledApp"
-    enum="NaClHttpStatusCodeClass" expires_after="2022-07-31">
+    enum="NaClHttpStatusCodeClass" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The status code returned when trying to load a manifest inside an installed
     app.
@@ -74,10 +69,9 @@
 </histogram>
 
 <histogram name="NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp"
-    enum="NaClHttpStatusCodeClass" expires_after="2022-07-31">
+    enum="NaClHttpStatusCodeClass" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The status code returned when trying to load a manifest from a source other
     than an installed app.
@@ -85,10 +79,9 @@
 </histogram>
 
 <histogram name="NaCl.HttpStatusCodeClass.Nexe.InstalledApp"
-    enum="NaClHttpStatusCodeClass" expires_after="2022-07-31">
+    enum="NaClHttpStatusCodeClass" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The status code returned when trying to load a NaCl executable inside an
     installed app.
@@ -96,10 +89,9 @@
 </histogram>
 
 <histogram name="NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp"
-    enum="NaClHttpStatusCodeClass" expires_after="2022-07-31">
+    enum="NaClHttpStatusCodeClass" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The status code returned when trying to load a NaCl executable from a source
     other than an installed app.
@@ -107,18 +99,16 @@
 </histogram>
 
 <histogram name="NaCl.LoadStatus.Plugin" enum="NaClPluginErrorCode"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The error code returned by NaCl's Chrome plugin.</summary>
 </histogram>
 
 <histogram name="NaCl.LoadStatus.Plugin.InstalledApp"
-    enum="NaClPluginErrorCode" expires_after="2022-07-31">
+    enum="NaClPluginErrorCode" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The error code returned by NaCl's Chrome plugin, but only for installed
     apps.
@@ -126,10 +116,9 @@
 </histogram>
 
 <histogram name="NaCl.LoadStatus.Plugin.NotInstalledApp"
-    enum="NaClPluginErrorCode" expires_after="2022-07-31">
+    enum="NaClPluginErrorCode" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The error code returned by NaCl's Chrome plugin, but excluding installed
     apps.
@@ -137,63 +126,56 @@
 </histogram>
 
 <histogram name="NaCl.LoadStatus.SelLdr" enum="NaClSelLdrErrorCode"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The error code returned by NaCl's sel_ldr.</summary>
 </histogram>
 
 <histogram name="NaCl.LoadStatus.SelLdr.InstalledApp"
-    enum="NaClSelLdrErrorCode" expires_after="2022-07-31">
+    enum="NaClSelLdrErrorCode" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The error code returned by NaCl's sel_ldr, but only for installed apps.
   </summary>
 </histogram>
 
 <histogram name="NaCl.LoadStatus.SelLdr.NotInstalledApp"
-    enum="NaClSelLdrErrorCode" expires_after="2022-07-31">
+    enum="NaClSelLdrErrorCode" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The error code returned by NaCl's sel_ldr, but excluding installed apps.
   </summary>
 </histogram>
 
 <histogram name="NaCl.Manifest.IsDataURI" enum="NaClManifestType"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     Was the manifest specified as a data URI rather than a .nmf file?
   </summary>
 </histogram>
 
-<histogram name="NaCl.ModuleUptime.Crash" units="ms" expires_after="2022-07-31">
+<histogram name="NaCl.ModuleUptime.Crash" units="ms" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The time a NaCl module ran before it crashed.</summary>
 </histogram>
 
 <histogram name="NaCl.ModuleUptime.Normal" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The time a NaCl module ran without crashing, at shutdown.</summary>
 </histogram>
 
 <histogram name="NaCl.Options.PNaCl.OptLevel" enum="PNaClOptionsOptLevelEnum"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The optimization level set for the initial Portable Native Client
     translation from bitcode to native code.
@@ -203,8 +185,7 @@
 <histogram name="NaCl.Perf.PNaClCache.IsHit" enum="PNaClTranslationCacheEnum"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     Did the Portable Native Client translation cache find an executable
     translated from bitcode?
@@ -214,8 +195,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.CompileKBPerSec" units="KB/s"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The rate for compiling a Portable Native Client bitcode file to an object
     file in Kilobytes per second.
@@ -225,8 +205,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.CompileTime" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to compile a Portable Native Client bitcode file to an
     object file.
@@ -236,8 +215,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.LinkTime" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to link a Portable Native Client generated object file into
     a Native Client executable.
@@ -247,8 +225,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.LoadCompiler" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to load and validate the Portable Native Client compiler.
   </summary>
@@ -257,8 +234,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.LoadLinker" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to load and validate the Portable Native Client linker.
   </summary>
@@ -267,8 +243,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded"
     units="%" expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The percentage of a Portable Native Client application that is compiled by
     the time the application is fully downloaded (compile and download happen in
@@ -279,8 +254,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec" units="KB/s"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The rate for completely translating a Portable Native Client bitcode file
     into a Native Client executable and caching the result in Kilobytes per
@@ -291,8 +265,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.TotalUncachedTime" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The total time it took to completely translate a Portable Native Client
     bitcode file into a Native Client executable, and cache the result.
@@ -302,31 +275,27 @@
 <histogram name="NaCl.Perf.ShutdownTime.Total" units="ms"
     expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The time it took the NaCl module to shut down.</summary>
 </histogram>
 
 <histogram name="NaCl.Perf.Size.Manifest" units="KB" expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The size of the manifest file.</summary>
 </histogram>
 
 <histogram name="NaCl.Perf.Size.Nexe" units="KB" expires_after="2022-10-04">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The size of the main .nexe file downloaded for a Native Client module.
   </summary>
 </histogram>
 
-<histogram name="NaCl.Perf.Size.Pexe" units="KB" expires_after="2022-07-31">
+<histogram name="NaCl.Perf.Size.Pexe" units="KB" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The size of the main .pexe bitcode file downloaded for a Portable Native
     Client module.
@@ -334,10 +303,9 @@
 </histogram>
 
 <histogram name="NaCl.Perf.Size.PexeNexeSizePct" units="%"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The size of the main .pexe bitcode file divided by the size of the .nexe
     that is the result of translating the bitcode file, times 100.
@@ -345,10 +313,9 @@
 </histogram>
 
 <histogram name="NaCl.Perf.Size.PNaClTranslatedNexe" units="KB"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The size of the main .nexe file that is the result of translating a Portable
     Native Client .pexe bitcode file. This reflects the amount of cache
@@ -357,18 +324,16 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.LoadModule" units="ms"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>The time it took to load the NaCl module into sel_ldr.</summary>
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.LoadModulePerMB" units="milliseconds/MB"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to load the NaCl module into sel_ldr. Normalized by the
     size of the .nexe, in megabytes.
@@ -376,20 +341,18 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.ManifestDownload" units="ms"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to download the manifset file for a Native Client module.
   </summary>
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.NaClOverhead" units="ms"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     proxied execution of the NaCl module begins. This is the general startup
@@ -398,10 +361,9 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.NaClOverheadPerMB"
-    units="milliseconds/MB" expires_after="2022-07-31">
+    units="milliseconds/MB" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     proxied execution of the NaCl module begins. This is the general startup
@@ -411,20 +373,18 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.NexeDownload" units="ms"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to download the main .nexe for a Native Client module.
   </summary>
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.NexeDownloadPerMB"
-    units="milliseconds/MB" expires_after="2022-07-31">
+    units="milliseconds/MB" expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took to download the main .nexe for a Native Client module.
     Normalized by the size of the .nexe, in megabytes.
@@ -432,10 +392,9 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.Total" units="ms"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     the NaCl module is ready to be used.
@@ -443,10 +402,9 @@
 </histogram>
 
 <histogram name="NaCl.Perf.StartupTime.TotalPerMB" units="milliseconds/MB"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     the NaCl module is ready to be used. Normalized by the size of the .nexe, in
@@ -455,20 +413,18 @@
 </histogram>
 
 <histogram name="NaCl.ValidationCache.Query" enum="NaClValidationCacheEnum"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     Did a validation cache query find a previously known validation result?
   </summary>
 </histogram>
 
 <histogram name="NaCl.ValidationCache.Set" enum="NaClValidationCacheEnum"
-    expires_after="2022-07-31">
+    expires_after="2023-07-31">
   <owner>dschuff@chromium.org</owner>
-  <owner>adamk@chromium.org</owner>
-  <owner>bbudge@chromium.org</owner>
+  <owner>fabiansommer@chromium.org</owner>
   <summary>
     Was the validation cache updated with a new validation result?
   </summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index 0c4b803..47695da 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -2336,7 +2336,7 @@
 
 <histogram
     name="PasswordManager.PasswordStoreAndroidBackend.ClearAllLocalPasswords.LoginsToRemove"
-    units="count" expires_after="2022-09-11">
+    units="count" expires_after="M108">
   <owner>fhorschig@chromium.org</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2346,7 +2346,7 @@
 
 <histogram
     name="PasswordManager.PasswordStoreAndroidBackend.ClearAllLocalPasswords.SuccessRate"
-    units="%" expires_after="2022-06-30">
+    units="%" expires_after="M108">
   <owner>fhorschig@chromium.org</owner>
   <owner>vsemeniuk@google.com</owner>
   <summary>
@@ -2359,7 +2359,7 @@
 
 <histogram
     name="PasswordManager.PasswordStoreAndroidBackend{Function}.APIError"
-    enum="PasswordStoreAndroidBackendAPIError" expires_after="2022-06-30">
+    enum="PasswordStoreAndroidBackendAPIError" expires_after="M108">
   <owner>fhorschig@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2380,7 +2380,7 @@
 
 <histogram
     name="PasswordManager.PasswordStoreAndroidBackend{Function}.ErrorCode"
-    enum="PasswordStoreAndroidBackendError" expires_after="2022-06-30">
+    enum="PasswordStoreAndroidBackendError" expires_after="M108">
   <owner>fhorschig@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2450,7 +2450,7 @@
 
 <histogram
     name="PasswordManager.PasswordStoreProxyBackend.{ModifyingFunction}.{Metric}.{Measurement}"
-    units="count" expires_after="2022-06-30">
+    units="count" expires_after="M108">
   <owner>fhorschig@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 2e78b62..87ae5f5 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -1274,7 +1274,7 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.TrustedVaultURLFetchResponse"
+<histogram name="Sync.TrustedVaultURLFetchResponse{Reason}"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2022-11-13">
   <owner>mmoskvitin@google.com</owner>
   <owner>mastiz@chromium.org</owner>
@@ -1284,6 +1284,15 @@
     Trusted Vault server (aka Security Domain Service). Note that requests that
     timed out are not covered by this histogram.
   </summary>
+  <token key="Reason">
+    <variant name="" summary="Any reason"/>
+    <variant name=".DownloadIsRecoverabilityDegraded"
+        summary="Downloading of the recoverability-degraded state"/>
+    <variant name=".DownloadKeys" summary="Downloading of keys"/>
+    <variant name=".RegisterDevice" summary="Local device registration"/>
+    <variant name=".RegisterUnspecifiedAuthenticationFactor"
+        summary="Registration of an authentication factor of unspecified type"/>
+  </token>
 </histogram>
 
 <histogram name="Sync.TypedURLDatabaseError" enum="SyncTypedUrlDatabaseError"
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index aa842a1..ca62fea 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -204,6 +204,21 @@
   </summary>
 </histogram>
 
+<histogram name="WebApk.Notification.PermissionRequestResult"
+    enum="ContentSetting" expires_after="2022-12-04">
+  <owner>mvanouwerkerk@chromium.org</owner>
+  <owner>peconn@chromium.org</owner>
+  <owner>
+    src/chrome/android/java/src/org/chromium/chrome/browser/webapps/OWNERS
+  </owner>
+  <summary>
+    The result of a WebAPK notification permission request for the purpose of
+    notification delegation. This is logged when the
+    NotificationPermissionUpdater receives the result from the
+    WebApkServiceClient.
+  </summary>
+</histogram>
+
 <histogram name="WebApk.Session.TotalDuration2{WebApkDistributorType}"
     units="ms" expires_after="2022-12-16">
   <owner>hartmanng@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b303920f..b29de0d 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,16 +5,16 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "35260c027b108d32894696c0bec1a676f326049f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d3300e34ed8ad844a4f18a9067cac38b0d950dbc/trace_processor_shell.exe"
+            "hash": "8cf237fe0a9e3b8d989b50a1cb37a5845328148e",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/2d2d50afec5365cdb39f1e9af40ab8fdde5387d8/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "01cb94f5b8bbe4fb86dc4f6b2fe40506a3ba0943",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/d3300e34ed8ad844a4f18a9067cac38b0d950dbc/trace_processor_shell"
+            "hash": "1a7a8e413609568ecf6a39e5f9b62f8bb35ed814",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/2d2d50afec5365cdb39f1e9af40ab8fdde5387d8/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "a19aa044a96d25ded789bdcd0e7cdf467d2acd0c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/9a4022db03b5b81c164e54183dd4e9ccd6b043e9/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/2d2d50afec5365cdb39f1e9af40ab8fdde5387d8/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/ime/ash/ime_engine_handler_interface.h b/ui/base/ime/ash/ime_engine_handler_interface.h
index 320b081b..ddf9f264 100644
--- a/ui/base/ime/ash/ime_engine_handler_interface.h
+++ b/ui/base/ime/ash/ime_engine_handler_interface.h
@@ -100,6 +100,9 @@
   // Called when the composition bounds changed.
   virtual void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) = 0;
 
+  // Called when caret bounds changed.
+  virtual void SetCaretBounds(const gfx::Rect& caret_bounds) = 0;
+
   // Gets the implementation of the keyboard controller.
   virtual ui::VirtualKeyboardController* GetVirtualKeyboardController()
       const = 0;
diff --git a/ui/base/ime/ash/input_method_ash.cc b/ui/base/ime/ash/input_method_ash.cc
index 9da2fc2..386db31 100644
--- a/ui/base/ime/ash/input_method_ash.cc
+++ b/ui/base/ime/ash/input_method_ash.cc
@@ -217,8 +217,11 @@
   DCHECK(client == GetTextInputClient());
   DCHECK(!IsTextInputTypeNone());
 
-  if (GetEngine())
-    GetEngine()->SetCompositionBounds(GetCompositionBounds(client));
+  ui::IMEEngineHandlerInterface* engine = GetEngine();
+  if (engine) {
+    engine->SetCompositionBounds(GetCompositionBounds(client));
+    engine->SetCaretBounds(client->GetCaretBounds());
+  }
 
   ash::IMECandidateWindowHandlerInterface* candidate_window =
       ui::IMEBridge::Get()->GetCandidateWindowHandler();
diff --git a/ui/base/ime/ash/mock_ime_engine_handler.cc b/ui/base/ime/ash/mock_ime_engine_handler.cc
index 5325a4bd..71201adf 100644
--- a/ui/base/ime/ash/mock_ime_engine_handler.cc
+++ b/ui/base/ime/ash/mock_ime_engine_handler.cc
@@ -57,6 +57,9 @@
 void MockIMEEngineHandler::SetCompositionBounds(
     const std::vector<gfx::Rect>& bounds) {}
 
+void MockIMEEngineHandler::SetCaretBounds(
+    const gfx::Rect& caret_bounds) {}
+
 ui::VirtualKeyboardController*
 MockIMEEngineHandler::GetVirtualKeyboardController() const {
   return nullptr;
diff --git a/ui/base/ime/ash/mock_ime_engine_handler.h b/ui/base/ime/ash/mock_ime_engine_handler.h
index 2d6ccc7c..2a30c73 100644
--- a/ui/base/ime/ash/mock_ime_engine_handler.h
+++ b/ui/base/ime/ash/mock_ime_engine_handler.h
@@ -31,6 +31,7 @@
   void ProcessKeyEvent(const ui::KeyEvent& key_event,
                        KeyEventDoneCallback callback) override;
   void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) override;
+  void SetCaretBounds(const gfx::Rect& caret_bounds) override;
   ui::VirtualKeyboardController* GetVirtualKeyboardController() const override;
   void PropertyActivate(const std::string& property_name) override;
   void CandidateClicked(uint32_t index) override;
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 25477209..ea93071 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -470,18 +470,6 @@
   <message name="IDS_FILE_BROWSER_GO_TO_FILE_LOCATION_BUTTON_LABEL" desc="Menu item label, navigating the user to the file's containing folder.">
     Go to file location
   </message>
-  <message name="IDS_FILE_BROWSER_OPEN_WITH_VERB_BUTTON_LABEL" desc="Verb that describe the action of opening one or more files or directories with the given extension.">
-    Open with <ph name="EXTENSION_NAME">$1<ex>Gallery</ex></ph>
-  </message>
-  <message name="IDS_FILE_BROWSER_ADD_TO_VERB_BUTTON_LABEL" desc="Verb that describes the uploading or addition of one or multiple files or directories to the given extension.">
-    Add to <ph name="EXTENSION_NAME">$1<ex>Evernote</ex></ph>
-  </message>
-  <message name="IDS_FILE_BROWSER_PACK_WITH_VERB_BUTTON_LABEL" desc="Verb that describes the packing or archiving of one or multiple files or directories with the given extension.">
-    Pack with <ph name="EXTENSION_NAME">$1<ex>ZIP</ex></ph>
-  </message>
-  <message name="IDS_FILE_BROWSER_SHARE_WITH_VERB_BUTTON_LABEL" desc="Verb that describes the action of sharing/attaching/sending files with the given application.">
-    Share with <ph name="EXTENSION_NAME">$1<ex>GMail</ex></ph>
-  </message>
   <message name="IDS_FILE_BROWSER_ZIP_SELECTION_BUTTON_LABEL" desc="Menu item label, showing dialog to create zip file for selected files.">
     Zip selection
   </message>
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index 4be957ee..f469d93 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -469,38 +469,6 @@
         task.iconType = 'generic';
       }
 
-      // Add verb to title.
-      if (task.verb) {
-        let verbButtonLabel = '';
-        switch (task.verb) {
-          case chrome.fileManagerPrivate.Verb.ADD_TO:
-            verbButtonLabel = 'ADD_TO_VERB_BUTTON_LABEL';
-            break;
-          case chrome.fileManagerPrivate.Verb.PACK_WITH:
-            verbButtonLabel = 'PACK_WITH_VERB_BUTTON_LABEL';
-            break;
-          case chrome.fileManagerPrivate.Verb.SHARE_WITH:
-            // Even when the task has SHARE_WITH verb, we don't prefix the title
-            // with "Share with" when the task is from SEND/SEND_MULTIPLE intent
-            // handlers from Android apps, since the title can already have an
-            // appropriate verb.
-            if (!(taskType == 'arc' &&
-                  (parsedActionId == 'send' ||
-                   parsedActionId == 'send_multiple'))) {
-              verbButtonLabel = 'SHARE_WITH_VERB_BUTTON_LABEL';
-            }
-            break;
-          case chrome.fileManagerPrivate.Verb.OPEN_WITH:
-            verbButtonLabel = 'OPEN_WITH_VERB_BUTTON_LABEL';
-            break;
-          default:
-            console.error('Invalid task verb: ' + task.verb);
-        }
-        if (verbButtonLabel) {
-          task.label = loadTimeData.getStringF(verbButtonLabel, task.title);
-        }
-      }
-
       result.push(task);
     }
 
diff --git a/ui/file_manager/integration_tests/file_manager/crostini.js b/ui/file_manager/integration_tests/file_manager/crostini.js
index 1aed4a75..b110fa44 100644
--- a/ui/file_manager/integration_tests/file_manager/crostini.js
+++ b/ui/file_manager/integration_tests/file_manager/crostini.js
@@ -131,13 +131,13 @@
   const appOptions = await remoteCall.callRemoteTestUtil(
       'queryAllElements', appId, ['#tasks-menu [tabindex]']);
   chrome.test.assertEq(
-      1, appOptions.filter(el => el.text == 'Open with App (Windows)').length);
+      1, appOptions.filter(el => el.text == 'App (Windows)').length);
 
   // Click on the Plugin VM app, and wait for error dialog.
-  await remoteCall.callRemoteTestUtil('fakeMouseClick', appId, [
-    `#tasks-menu [tabindex]:nth-of-type(${
-        appOptions.map(el => el.text).indexOf('Open with App (Windows)') + 1})`
-  ]);
+  await remoteCall.callRemoteTestUtil(
+      'fakeMouseClick', appId,
+      [`#tasks-menu [tabindex]:nth-of-type(${
+          appOptions.map(el => el.text).indexOf('App (Windows)') + 1})`]);
   await remoteCall.waitUntilTaskExecutes(
       appId, pluginVmAppDescriptor, ['failed_plugin_vm_directory_not_shared']);
   await remoteCall.waitForElement(
@@ -206,13 +206,13 @@
   const appOptions = await remoteCall.callRemoteTestUtil(
       'queryAllElements', appId, ['#tasks-menu [tabindex]']);
   chrome.test.assertEq(
-      1, appOptions.filter(el => el.text == 'Open with App (Windows)').length);
+      1, appOptions.filter(el => el.text == 'App (Windows)').length);
 
   // Click on the Plugin VM app, and wait for error dialog.
-  await remoteCall.callRemoteTestUtil('fakeMouseClick', appId, [
-    `#tasks-menu [tabindex]:nth-of-type(${
-        appOptions.map(el => el.text).indexOf('Open with App (Windows)') + 1})`
-  ]);
+  await remoteCall.callRemoteTestUtil(
+      'fakeMouseClick', appId,
+      [`#tasks-menu [tabindex]:nth-of-type(${
+          appOptions.map(el => el.text).indexOf('App (Windows)') + 1})`]);
   await remoteCall.waitUntilTaskExecutes(
       appId, pluginVmAppDescriptor, ['failed_plugin_vm_directory_not_shared']);
   await remoteCall.waitForElement(
diff --git a/ui/views/view_model.cc b/ui/views/view_model.cc
index d059ecc6..e3ba79f 100644
--- a/ui/views/view_model.cc
+++ b/ui/views/view_model.cc
@@ -15,14 +15,19 @@
   // view are owned by their parent, no need to delete them.
 }
 
-void ViewModelBase::Remove(size_t index) {
+void ViewModelBase::Remove(int index) {
+  if (index == -1)
+    return;
+
   check_index(index);
   entries_.erase(entries_.begin() + index);
 }
 
-void ViewModelBase::Move(size_t index, size_t target_index) {
-  check_index(index);
-  check_index(target_index);
+void ViewModelBase::Move(int index, int target_index) {
+  DCHECK_LT(index, static_cast<int>(entries_.size()));
+  DCHECK_GE(index, 0);
+  DCHECK_LT(target_index, static_cast<int>(entries_.size()));
+  DCHECK_GE(target_index, 0);
 
   if (index == target_index)
     return;
@@ -31,15 +36,15 @@
   entries_.insert(entries_.begin() + target_index, entry);
 }
 
-void ViewModelBase::MoveViewOnly(size_t index, size_t target_index) {
+void ViewModelBase::MoveViewOnly(int index, int target_index) {
   if (target_index < index) {
     View* view = entries_[index].view;
-    for (size_t i = index; i > target_index; --i)
+    for (int i = index; i > target_index; --i)
       entries_[i].view = entries_[i - 1].view;
     entries_[target_index].view = view;
   } else if (target_index > index) {
     View* view = entries_[index].view;
-    for (size_t i = index; i < target_index; ++i)
+    for (int i = index; i < target_index; ++i)
       entries_[i].view = entries_[i + 1].view;
     entries_[target_index].view = view;
   }
@@ -52,18 +57,18 @@
     delete entry.view;
 }
 
-absl::optional<size_t> ViewModelBase::GetIndexOfView(const View* view) const {
+int ViewModelBase::GetIndexOfView(const View* view) const {
   const auto i =
       std::find_if(entries_.cbegin(), entries_.cend(),
                    [view](const auto& entry) { return entry.view == view; });
-  return (i == entries_.cend()) ? absl::nullopt
-                                : absl::make_optional(i - entries_.cbegin());
+  return (i == entries_.cend()) ? -1 : (i - entries_.cbegin());
 }
 
 ViewModelBase::ViewModelBase() = default;
 
-void ViewModelBase::AddUnsafe(View* view, size_t index) {
-  check_index(index);
+void ViewModelBase::AddUnsafe(View* view, int index) {
+  DCHECK_LE(index, static_cast<int>(entries_.size()));
+  DCHECK_GE(index, 0);
   Entry entry;
   entry.view = view;
   entries_.insert(entries_.begin() + index, entry);
diff --git a/ui/views/view_model.h b/ui/views/view_model.h
index e8eeb73..ae64bae 100644
--- a/ui/views/view_model.h
+++ b/ui/views/view_model.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "base/check_op.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/views_export.h"
 
@@ -41,37 +40,37 @@
 
   // Removes the view at the specified index. This does not actually remove the
   // view from the view hierarchy.
-  void Remove(size_t index);
+  void Remove(int index);
 
   // Moves the view at |index| to |target_index|. |target_index| is in terms
   // of the model *after* the view at |index| is removed.
-  void Move(size_t index, size_t target_index);
+  void Move(int index, int target_index);
 
   // Variant of Move() that leaves the bounds as is. That is, after invoking
   // this the bounds of the view at |target_index| (and all other indices) are
   // exactly the same as the bounds of the view at |target_index| before
   // invoking this.
-  void MoveViewOnly(size_t index, size_t target_index);
+  void MoveViewOnly(int index, int target_index);
 
   // Returns the number of views.
-  size_t view_size() const { return entries_.size(); }
+  int view_size() const { return static_cast<int>(entries_.size()); }
 
   // Removes and deletes all the views.
   void Clear();
 
-  void set_ideal_bounds(size_t index, const gfx::Rect& bounds) {
+  void set_ideal_bounds(int index, const gfx::Rect& bounds) {
     check_index(index);
     entries_[index].ideal_bounds = bounds;
   }
 
-  const gfx::Rect& ideal_bounds(size_t index) const {
+  const gfx::Rect& ideal_bounds(int index) const {
     check_index(index);
     return entries_[index].ideal_bounds;
   }
 
-  // Returns the index of the specified view, or nullopt if the view isn't in
-  // the model.
-  absl::optional<size_t> GetIndexOfView(const View* view) const;
+  // Returns the index of the specified view, or -1 if the view isn't in the
+  // model.
+  int GetIndexOfView(const View* view) const;
 
  protected:
   ViewModelBase();
@@ -79,23 +78,26 @@
   // Returns the view at the specified index. Note: Most users should use
   // view_at() in the subclass, to get a view of the correct type. (Do not call
   // ViewAtBase then static_cast to the desired type.)
-  View* ViewAtBase(size_t index) const {
+  View* ViewAtBase(int index) const {
     check_index(index);
     return entries_[index].view;
   }
 
   // Adds |view| to this model. This does not add |view| to a view hierarchy,
   // only to this model.
-  void AddUnsafe(View* view, size_t index);
+  void AddUnsafe(View* view, int index);
 
  private:
   // For access to ViewAtBase().
   friend class ViewModelUtils;
 
 #if !defined(NDEBUG)
-  void check_index(size_t index) const { DCHECK_LT(index, entries_.size()); }
+  void check_index(int index) const {
+    DCHECK_LT(index, static_cast<int>(entries_.size()));
+    DCHECK_GE(index, 0);
+  }
 #else
-  void check_index(size_t index) const {}
+  void check_index(int index) const {}
 #endif
 
   Entries entries_;
@@ -116,10 +118,10 @@
 
   // Adds |view| to this model. This does not add |view| to a view hierarchy,
   // only to this model.
-  void Add(T* view, size_t index) { AddUnsafe(view, index); }
+  void Add(T* view, int index) { AddUnsafe(view, index); }
 
   // Returns the view at the specified index.
-  T* view_at(size_t index) const { return static_cast<T*>(ViewAtBase(index)); }
+  T* view_at(int index) const { return static_cast<T*>(ViewAtBase(index)); }
 };
 
 // ViewModel is a collection of views with no specfic type. If all views have
diff --git a/ui/views/view_model_unittest.cc b/ui/views/view_model_unittest.cc
index 169d17b..a922bfc 100644
--- a/ui/views/view_model_unittest.cc
+++ b/ui/views/view_model_unittest.cc
@@ -17,7 +17,7 @@
 // Returns a string containing the x-coordinate of each of the views in |model|.
 std::string BoundsString(const ViewModel& model) {
   std::string result;
-  for (size_t i = 0; i < model.view_size(); ++i) {
+  for (int i = 0; i < model.view_size(); ++i) {
     if (i != 0)
       result += " ";
     result += base::NumberToString(model.ideal_bounds(i).x());
@@ -28,7 +28,7 @@
 // Returns a string containing the id of each of the views in |model|.
 std::string ViewIDsString(const ViewModel& model) {
   std::string result;
-  for (size_t i = 0; i < model.view_size(); ++i) {
+  for (int i = 0; i < model.view_size(); ++i) {
     if (i != 0)
       result += " ";
     result += base::NumberToString(model.view_at(i)->GetID());
@@ -42,12 +42,12 @@
   View v1;
   ViewModel model;
   model.Add(&v1, 0);
-  EXPECT_EQ(1u, model.view_size());
+  EXPECT_EQ(1, model.view_size());
   EXPECT_EQ(&v1, model.view_at(0));
   gfx::Rect v1_bounds(1, 2, 3, 4);
   model.set_ideal_bounds(0, v1_bounds);
   EXPECT_EQ(v1_bounds, model.ideal_bounds(0));
-  EXPECT_EQ(0u, model.GetIndexOfView(&v1));
+  EXPECT_EQ(0, model.GetIndexOfView(&v1));
 }
 
 TEST(ViewModel, Move) {
diff --git a/ui/views/view_model_utils.cc b/ui/views/view_model_utils.cc
index 3e1a71a..8788a7b 100644
--- a/ui/views/view_model_utils.cc
+++ b/ui/views/view_model_utils.cc
@@ -35,25 +35,25 @@
 }
 
 // static
-size_t ViewModelUtils::DetermineMoveIndex(const ViewModelBase& model,
-                                          View* view,
-                                          bool is_horizontal,
-                                          int x,
-                                          int y) {
+int ViewModelUtils::DetermineMoveIndex(const ViewModelBase& model,
+                                       View* view,
+                                       bool is_horizontal,
+                                       int x,
+                                       int y) {
   const auto& entries = model.entries();
   const int value = primary_axis_coordinate(is_horizontal, gfx::Point(x, y));
-  DCHECK(model.GetIndexOfView(view).has_value());
+  DCHECK_NE(-1, model.GetIndexOfView(view));
 
   auto iter = entries.begin();
   for (; iter->view != view; ++iter) {
     const int mid_point = primary_axis_coordinate(
         is_horizontal, iter->ideal_bounds.CenterPoint());
     if (value < mid_point)
-      return static_cast<size_t>(std::distance(entries.begin(), iter));
+      return std::distance(entries.begin(), iter);
   }
 
   if (std::next(iter) == entries.end())
-    return static_cast<size_t>(std::distance(entries.begin(), iter));
+    return std::distance(entries.begin(), iter);
 
   // For indices after the current index ignore the bounds of the view being
   // dragged. This keeps the view from bouncing around as moved.
@@ -66,7 +66,7 @@
                               is_horizontal, iter->ideal_bounds.CenterPoint()) -
                           delta;
     if (value < mid_point)
-      return static_cast<size_t>(std::distance(entries.begin(), iter)) - 1;
+      return std::distance(entries.begin(), iter) - 1;
   }
   return entries.size() - 1;
 }
diff --git a/ui/views/view_model_utils.h b/ui/views/view_model_utils.h
index e33d5ff..af200bc 100644
--- a/ui/views/view_model_utils.h
+++ b/ui/views/view_model_utils.h
@@ -5,8 +5,6 @@
 #ifndef UI_VIEWS_VIEW_MODEL_UTILS_H_
 #define UI_VIEWS_VIEW_MODEL_UTILS_H_
 
-#include <stddef.h>
-
 #include "ui/views/views_export.h"
 
 namespace views {
@@ -27,11 +25,11 @@
   static bool IsAtIdealBounds(const ViewModelBase& model);
 
   // Returns the index to move |view| to based on a coordinate of |x| and |y|.
-  static size_t DetermineMoveIndex(const ViewModelBase& model,
-                                   View* view,
-                                   bool is_horizontal,
-                                   int x,
-                                   int y);
+  static int DetermineMoveIndex(const ViewModelBase& model,
+                                View* view,
+                                bool is_horizontal,
+                                int x,
+                                int y);
 };
 
 }  // namespace views
diff --git a/ui/views/view_model_utils_unittest.cc b/ui/views/view_model_utils_unittest.cc
index 2895ac9..5b9349e 100644
--- a/ui/views/view_model_utils_unittest.cc
+++ b/ui/views/view_model_utils_unittest.cc
@@ -32,30 +32,30 @@
   model.set_ideal_bounds(1, gfx::Rect(10, 0, 1000, 10));
   model.set_ideal_bounds(2, gfx::Rect(1010, 0, 2, 10));
 
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v1, true, -10, 0));
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 4, 0));
-  EXPECT_EQ(1u, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 506, 0));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 1010, 0));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 2000, 0));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, true, -10, 0));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 4, 0));
+  EXPECT_EQ(1, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 506, 0));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 1010, 0));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, true, 2000, 0));
 
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v2, true, -10, 0));
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v2, true, 4, 0));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v2, true, 12, 0));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, true, -10, 0));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, true, 4, 0));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v2, true, 12, 0));
 
   // Try the same when vertical.
   model.set_ideal_bounds(0, gfx::Rect(0, 0, 10, 10));
   model.set_ideal_bounds(1, gfx::Rect(0, 10, 10, 1000));
   model.set_ideal_bounds(2, gfx::Rect(0, 1010, 10, 2));
 
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, -10));
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 4));
-  EXPECT_EQ(1u, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 506));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 1010));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 2000));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, -10));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 4));
+  EXPECT_EQ(1, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 506));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 1010));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v1, false, 0, 2000));
 
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, -10));
-  EXPECT_EQ(0u, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, 4));
-  EXPECT_EQ(2u, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, 12));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, -10));
+  EXPECT_EQ(0, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, 4));
+  EXPECT_EQ(2, ViewModelUtils::DetermineMoveIndex(model, &v2, false, 0, 12));
 }
 
 }  // namespace views