diff --git a/.gn b/.gn
index 8834ee9..fdde7824 100644
--- a/.gn
+++ b/.gn
@@ -71,7 +71,6 @@
 # "gn check" or "gn gen --check".
 no_check_targets = [
   "//extensions:chrome_extensions_browsertests",  # 26 errors
-  "//extensions:chrome_extensions_interactive_uitests",  # 2 errors
 
   "//headless:headless_browsertests",  # 47 errors
   "//headless:headless_browsertests__exec",
diff --git a/DEPS b/DEPS
index 72e6ba1..3399729b 100644
--- a/DEPS
+++ b/DEPS
@@ -239,11 +239,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': '06f3d68627c28d3d5faeb45a289acb17150762a3',
+  'skia_revision': '543b8681c7f27138932ab132a41b7a1631b8c4c8',
   # 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': 'b560905d0b45c4a963329318bb38c8d8ec458f49',
+  'v8_revision': '63f87dc92e4411c994b1b2dc758377a2b168bb39',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -314,7 +314,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': 'b154da2d039abadf3dbedabaddf621981faeedbc',
+  'devtools_frontend_revision': 'ffedc3ae5ea8cc97c83e69ddcaf15e0ebed5f8a2',
   # 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.
@@ -354,7 +354,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': '0766ce6ed1b607ad8353d6bf93e456dcf32c0e0a',
+  'dawn_revision': '8b9e591f48b44673ab83936a7ebf18acdb2dd1b6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -796,7 +796,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '5lV65l1RfT7BHK1JOXfWWtQhT1aL7x-_2IYlhuxEQisC',
+          'version': 'KOLlPoehy50pZsVEr88FIh8mpg_MDPsZelkCRMsVZwQC',
       },
     ],
     'condition': 'checkout_android',
@@ -927,7 +927,7 @@
 
   'src/third_party/barhopper': {
       'url': 'https://chrome-internal.googlesource.com/chrome/deps/barhopper.git' + '@' + 'b619dfad3ef48aa15d3a647442c3c40f3a967146',
-      'condition': 'checkout_src_internal',
+      'condition': 'checkout_src_internal and checkout_chromeos',
   },
 
   'src/third_party/cast_core/prebuilts': {
@@ -961,7 +961,7 @@
   },
 
   'src/third_party/breakpad/breakpad':
-    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '0ae29c99d1a0abed797ad78e5e061f4e2cb9c1cb',
+    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '647aa17a7aa8ec0b99ffd005908b8a4ab1995a30',
 
   'src/third_party/byte_buddy': {
       'packages': [
@@ -1035,7 +1035,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1693c67f39cc03c883a3821de6e58cfb75f35370',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '45e67e97f851a6511bc9032e83326fb734159731',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1697,7 +1697,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@32e38a05454f1bdbd75cbe0868d87378efcbe8e0',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8154650bd62725728c39193d6bf9e613a32259ea',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index d71a985..2957cc19 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -258,6 +258,7 @@
     "app_list_unittest.cc",
     "folder_image_unittest.cc",
     "model/app_list_item_list_unittest.cc",
+    "model/app_list_item_unittest.cc",
     "model/app_list_model_unittest.cc",
     "views/app_drag_icon_proxy_unittest.cc",
     "views/app_list_bubble_apps_page_unittest.cc",
diff --git a/ash/app_list/folder_image_unittest.cc b/ash/app_list/folder_image_unittest.cc
index 1c4b6602..66adf9f3 100644
--- a/ash/app_list/folder_image_unittest.cc
+++ b/ash/app_list/folder_image_unittest.cc
@@ -12,6 +12,7 @@
 #include "ash/app_list/model/app_list_test_model.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_config_provider.h"
+#include "base/test/icu_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -56,11 +57,11 @@
 
 }  // namespace
 
-class FolderImageTest
-    : public testing::Test,
-      public ::testing::WithParamInterface<AppListConfigType> {
+class FolderImageTest : public testing::Test,
+                        public ::testing::WithParamInterface<
+                            std::tuple<AppListConfigType, bool>> {
  public:
-  FolderImageTest() = default;
+  FolderImageTest() : scoped_locale_(std::get<1>(GetParam()) ? "he" : "") {}
 
   FolderImageTest(const FolderImageTest&) = delete;
   FolderImageTest& operator=(const FolderImageTest&) = delete;
@@ -69,9 +70,9 @@
 
   void SetUp() override {
     app_list_model_ = std::make_unique<test::AppListTestModel>();
-    folder_image_ = std::make_unique<FolderImage>(
-        AppListConfigProvider::Get().GetConfigForType(GetParam(), true),
-        app_list_model_->top_level_item_list());
+    folder_image_ =
+        std::make_unique<FolderImage>(GetAppListConfig(/*can_create=*/true),
+                                      app_list_model_->top_level_item_list());
 
     // Populate the AppListModel with three items (to test that the FolderImage
     // correctly supports having fewer than four icons).
@@ -88,6 +89,13 @@
     AppListConfigProvider::Get().ResetForTesting();
   }
 
+  AppListConfig* GetAppListConfig(bool can_create) {
+    return AppListConfigProvider::Get().GetConfigForType(
+        std::get<0>(GetParam()), can_create);
+  }
+
+  bool is_rtl() { return std::get<1>(GetParam()); }
+
  protected:
   void AddAppWithColoredIcon(const std::string& id, SkColor icon_color) {
     std::unique_ptr<AppListItem> item(new AppListItem(id));
@@ -97,17 +105,21 @@
     static_cast<AppListModel*>(app_list_model_.get())->AddItem(std::move(item));
   }
 
+  base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
+
   std::unique_ptr<test::AppListTestModel> app_list_model_;
 
   std::unique_ptr<FolderImage> folder_image_;
 
   TestFolderImageObserver observer_;
 };
-INSTANTIATE_TEST_SUITE_P(All,
-                         FolderImageTest,
-                         ::testing::Values(AppListConfigType::kLarge,
-                                           AppListConfigType::kMedium,
-                                           AppListConfigType::kSmall));
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    FolderImageTest,
+    ::testing::Combine(::testing::Values(AppListConfigType::kLarge,
+                                         AppListConfigType::kMedium,
+                                         AppListConfigType::kSmall),
+                       ::testing::Bool()));
 
 TEST_P(FolderImageTest, UpdateListTest) {
   gfx::ImageSkia icon1 = folder_image_->icon();
@@ -169,8 +181,7 @@
 TEST_P(FolderImageTest, GetTargetIconRectInFolderWithSingleItem) {
   app_list_model_->DeleteItem("app2");
   app_list_model_->DeleteItem("app3");
-  const AppListConfig* config =
-      AppListConfigProvider::Get().GetConfigForType(GetParam(), false);
+  const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
   ASSERT_TRUE(config);
 
   const gfx::Rect test_rects[] = {
@@ -199,8 +210,7 @@
 
 TEST_P(FolderImageTest, GetTargetIconRectInFolderWithTwoItems) {
   app_list_model_->DeleteItem("app3");
-  const AppListConfig* config =
-      AppListConfigProvider::Get().GetConfigForType(GetParam(), false);
+  const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
   ASSERT_TRUE(config);
 
   const gfx::Rect test_rects[] = {
@@ -222,14 +232,18 @@
 
     gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
         *config, app_list_model_->FindItem("app1"), test_rect);
+    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app2"), test_rect);
+
+    if (is_rtl())
+      std::swap(item_1_bounds, item_2_bounds);
+
     EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
         item_1_bounds.right());
     EXPECT_EQ(test_rect_center.y(), item_1_bounds.CenterPoint().y());
 
-    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app2"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -239,8 +253,7 @@
 }
 
 TEST_P(FolderImageTest, GetTargetIconRectInFolderWithThreeItems) {
-  const AppListConfig* config =
-      AppListConfigProvider::Get().GetConfigForType(GetParam(), false);
+  const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
   ASSERT_TRUE(config);
 
   const gfx::Rect test_rects[] = {
@@ -262,6 +275,13 @@
 
     gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
         *config, app_list_model_->FindItem("app1"), test_rect);
+    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app2"), test_rect);
+    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app3"), test_rect);
+
+    if (is_rtl())
+      std::swap(item_1_bounds, item_2_bounds);
     EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
@@ -270,8 +290,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_1_bounds.bottom());
 
-    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app2"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -280,8 +298,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_2_bounds.bottom());
 
-    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app3"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_3_bounds.size());
     EXPECT_EQ(test_rect_center.x(), item_3_bounds.CenterPoint().x());
     EXPECT_EQ(
@@ -293,8 +309,7 @@
 TEST_P(FolderImageTest, GetTargetIconRectInFolderWithFourItems) {
   AddAppWithColoredIcon("app4", SK_ColorYELLOW);
 
-  const AppListConfig* config =
-      AppListConfigProvider::Get().GetConfigForType(GetParam(), false);
+  const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
   ASSERT_TRUE(config);
 
   const gfx::Rect test_rects[] = {
@@ -316,6 +331,18 @@
 
     gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
         *config, app_list_model_->FindItem("app1"), test_rect);
+    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app2"), test_rect);
+    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app3"), test_rect);
+    gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app4"), test_rect);
+
+    if (is_rtl()) {
+      std::swap(item_1_bounds, item_2_bounds);
+      std::swap(item_3_bounds, item_4_bounds);
+    }
+
     EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
@@ -324,8 +351,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_1_bounds.bottom());
 
-    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app2"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -334,8 +359,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_2_bounds.bottom());
 
-    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app3"), test_rect);
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
         item_3_bounds.right());
@@ -343,8 +366,6 @@
         test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
         item_3_bounds.y());
 
-    gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app4"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_4_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -359,8 +380,7 @@
   AddAppWithColoredIcon("app4", SK_ColorYELLOW);
   AddAppWithColoredIcon("app5", SK_ColorYELLOW);
 
-  const AppListConfig* config =
-      AppListConfigProvider::Get().GetConfigForType(GetParam(), false);
+  const AppListConfig* config = GetAppListConfig(/*can_create=*/false);
   ASSERT_TRUE(config);
 
   const gfx::Rect test_rects[] = {
@@ -382,6 +402,20 @@
 
     gfx::Rect item_1_bounds = folder_image_->GetTargetIconRectInFolderForItem(
         *config, app_list_model_->FindItem("app1"), test_rect);
+    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app2"), test_rect);
+    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app3"), test_rect);
+    gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app4"), test_rect);
+    gfx::Rect item_5_bounds = folder_image_->GetTargetIconRectInFolderForItem(
+        *config, app_list_model_->FindItem("app5"), test_rect);
+
+    if (is_rtl()) {
+      std::swap(item_1_bounds, item_2_bounds);
+      std::swap(item_3_bounds, item_4_bounds);
+    }
+
     EXPECT_EQ(expected_icon_rect_size, item_1_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
@@ -390,8 +424,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_1_bounds.bottom());
 
-    gfx::Rect item_2_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app2"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_2_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -400,8 +432,6 @@
         test_rect_center.y() - config->item_icon_in_folder_icon_margin() / 2,
         item_2_bounds.bottom());
 
-    gfx::Rect item_3_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app3"), test_rect);
     EXPECT_EQ(
         test_rect_center.x() - config->item_icon_in_folder_icon_margin() / 2,
         item_3_bounds.right());
@@ -409,8 +439,6 @@
         test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
         item_3_bounds.y());
 
-    gfx::Rect item_4_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app4"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_4_bounds.size());
     EXPECT_EQ(
         test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
@@ -419,12 +447,7 @@
         test_rect_center.y() + config->item_icon_in_folder_icon_margin() / 2,
         item_4_bounds.y());
 
-    gfx::Rect item_5_bounds = folder_image_->GetTargetIconRectInFolderForItem(
-        *config, app_list_model_->FindItem("app5"), test_rect);
     EXPECT_EQ(expected_icon_rect_size, item_5_bounds.size());
-    EXPECT_EQ(
-        test_rect_center.x() + config->item_icon_in_folder_icon_margin() / 2,
-        item_4_bounds.x());
     EXPECT_EQ(test_rect_center, item_5_bounds.CenterPoint());
   }
 }
diff --git a/ash/app_list/model/app_list_item.cc b/ash/app_list/model/app_list_item.cc
index d8dffd2..7696157 100644
--- a/ash/app_list/model/app_list_item.cc
+++ b/ash/app_list/model/app_list_item.cc
@@ -157,4 +157,13 @@
   }
 }
 
+void AppListItem::SetIsNewInstall(bool is_new_install) {
+  if (metadata_->is_new_install == is_new_install)
+    return;
+
+  metadata_->is_new_install = is_new_install;
+  for (auto& observer : observers_) {
+    observer.ItemIsNewInstallChanged();
+  }
+}
 }  // namespace ash
diff --git a/ash/app_list/model/app_list_item.h b/ash/app_list/model/app_list_item.h
index 20be0ba..31f558a 100644
--- a/ash/app_list/model/app_list_item.h
+++ b/ash/app_list/model/app_list_item.h
@@ -30,13 +30,9 @@
 // and action to be executed when the AppListItemView is activated.
 class APP_LIST_MODEL_EXPORT AppListItem {
  public:
-  using AppListItemMetadata = ash::AppListItemMetadata;
-
   explicit AppListItem(const std::string& id);
-
   AppListItem(const AppListItem&) = delete;
   AppListItem& operator=(const AppListItem&) = delete;
-
   virtual ~AppListItem();
 
   void SetIcon(AppListConfigType config_type, const gfx::ImageSkia& icon);
@@ -114,12 +110,17 @@
 
   SkColor notification_badge_color() const { return metadata_->badge_color; }
 
+  bool is_new_install() const { return metadata_->is_new_install; }
+
+  // Sets the `is_new_install` metadata field and notifies observers.
+  void SetIsNewInstall(bool is_new_install);
+
+  AppStatus app_status() const { return metadata_->app_status; }
+
   void UpdateNotificationBadgeForTesting(bool has_badge) {
     UpdateNotificationBadge(has_badge);
   }
 
-  AppStatus app_status() const { return metadata_->app_status; }
-
   void UpdateAppStatusForTesting(AppStatus app_status) {
     metadata_->app_status = app_status;
   }
diff --git a/ash/app_list/model/app_list_item_observer.h b/ash/app_list/model/app_list_item_observer.h
index 3ce6324..443d240 100644
--- a/ash/app_list/model/app_list_item_observer.h
+++ b/ash/app_list/model/app_list_item_observer.h
@@ -30,6 +30,9 @@
   // Invoked when the item's notification badge color is changed.
   virtual void ItemBadgeColorChanged() {}
 
+  // Invoked when the item's "new install" badge is added or removed.
+  virtual void ItemIsNewInstallChanged() {}
+
   // Invoked when the item is about to be destroyed.
   virtual void ItemBeingDestroyed() {}
 
diff --git a/ash/app_list/model/app_list_item_unittest.cc b/ash/app_list/model/app_list_item_unittest.cc
new file mode 100644
index 0000000..daefa7f
--- /dev/null
+++ b/ash/app_list/model/app_list_item_unittest.cc
@@ -0,0 +1,40 @@
+// 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 "ash/app_list/model/app_list_item.h"
+
+#include "ash/app_list/model/app_list_item_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+TEST(AppListItemTest, SetIsNewInstall) {
+  AppListItem item("id");
+
+  class Observer : public AppListItemObserver {
+   public:
+    // AppListItemObserver:
+    void ItemIsNewInstallChanged() override { is_new_install_changed_++; }
+
+    int is_new_install_changed_ = 0;
+  } observer;
+
+  // Adding an observer does not notify it.
+  item.AddObserver(&observer);
+  EXPECT_EQ(observer.is_new_install_changed_, 0);
+
+  // Setting new install notifies the observer.
+  item.SetIsNewInstall(true);
+  EXPECT_TRUE(item.is_new_install());
+  EXPECT_EQ(observer.is_new_install_changed_, 1);
+
+  // Clearing new install notifies the observer.
+  item.SetIsNewInstall(false);
+  EXPECT_FALSE(item.is_new_install());
+  EXPECT_EQ(observer.is_new_install_changed_, 2);
+
+  item.RemoveObserver(&observer);
+}
+
+}  // namespace ash
diff --git a/ash/app_list/model/folder_image.cc b/ash/app_list/model/folder_image.cc
index ee90882..865ca4a8 100644
--- a/ash/app_list/model/folder_image.cc
+++ b/ash/app_list/model/folder_image.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "ash/app_list/model/app_list_item.h"
@@ -13,6 +14,7 @@
 #include "ash/public/cpp/app_list/app_list_color_provider.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_config_provider.h"
+#include "base/i18n/rtl.h"
 #include "base/memory/ptr_util.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
@@ -251,11 +253,15 @@
     // Left icon bounds.
     gfx::Rect left_rect = center_rect;
     left_rect.Offset(-origin_offset, 0);
-    top_icon_bounds.emplace_back(scale_and_translate_bounds(left_rect));
 
     // Right icon bounds.
     gfx::Rect right_rect = center_rect;
     right_rect.Offset(origin_offset, 0);
+
+    if (base::i18n::IsRTL())
+      std::swap(left_rect, right_rect);
+
+    top_icon_bounds.emplace_back(scale_and_translate_bounds(left_rect));
     top_icon_bounds.emplace_back(scale_and_translate_bounds(right_rect));
     return top_icon_bounds;
   }
@@ -263,11 +269,15 @@
   // Top left icon bounds.
   gfx::Rect top_left_rect = center_rect;
   top_left_rect.Offset(-origin_offset, -origin_offset);
-  top_icon_bounds.emplace_back(scale_and_translate_bounds(top_left_rect));
 
   // Top right icon bounds.
   gfx::Rect top_right_rect = center_rect;
   top_right_rect.Offset(origin_offset, -origin_offset);
+
+  if (base::i18n::IsRTL())
+    std::swap(top_left_rect, top_right_rect);
+
+  top_icon_bounds.emplace_back(scale_and_translate_bounds(top_left_rect));
   top_icon_bounds.emplace_back(scale_and_translate_bounds(top_right_rect));
 
   if (num_items == 3) {
@@ -281,11 +291,15 @@
   // Bottom left icon bounds.
   gfx::Rect bottom_left_rect = center_rect;
   bottom_left_rect.Offset(-origin_offset, origin_offset);
-  top_icon_bounds.emplace_back(scale_and_translate_bounds(bottom_left_rect));
 
   // Bottom right icon bounds.
   gfx::Rect bottom_right_rect = center_rect;
   bottom_right_rect.Offset(origin_offset, origin_offset);
+
+  if (base::i18n::IsRTL())
+    std::swap(bottom_left_rect, bottom_right_rect);
+
+  top_icon_bounds.emplace_back(scale_and_translate_bounds(bottom_left_rect));
   top_icon_bounds.emplace_back(scale_and_translate_bounds(bottom_right_rect));
   return top_icon_bounds;
 }
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index cc1e52f..b2066729 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -435,7 +435,8 @@
       // Return the item bounds in AppListFolderView coordinates.
       gfx::RectF bounds_in_folder(item->GetLocalBounds());
       views::View::ConvertRectToTarget(item, folder_view_, &bounds_in_folder);
-      items_bounds.emplace_back(gfx::ToRoundedRect(bounds_in_folder));
+      items_bounds.emplace_back(
+          folder_view_->GetMirroredRect(gfx::ToRoundedRect(bounds_in_folder)));
     }
     return items_bounds;
   }
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index bdfc561..5b72ee501 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -5,6 +5,7 @@
 #include "ash/app_list/views/app_list_item_view.h"
 
 #include <algorithm>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -585,10 +586,16 @@
   const std::u16string folder_name_placeholder =
       ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
           IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER);
-  if (is_folder_ && display_name.empty())
+  if (is_folder_ && display_name.empty()) {
     title_->SetText(folder_name_placeholder);
-  else
-    title_->SetText(display_name);
+  } else {
+    std::u16string title_text;
+    // TODO(crbug.com/1272817): Use a view for the dot.
+    if (item() && item()->is_new_install())
+      title_text = u"• ";
+    title_text += display_name;
+    title_->SetText(title_text);
+  }
 
   tooltip_text_ = display_name == full_name ? std::u16string() : full_name;
 
@@ -1155,6 +1162,13 @@
     notification_indicator_->SetColor(item_weak_->notification_badge_color());
 }
 
+void AppListItemView::ItemIsNewInstallChanged() {
+  // TODO(crbug.com/1272817): Use a view for the dot. For now, just refresh the
+  // label to add or remove the bullet from the name.
+  SetItemName(base::UTF8ToUTF16(item_weak_->GetDisplayName()),
+              base::UTF8ToUTF16(item_weak_->name()));
+}
+
 void AppListItemView::ItemBeingDestroyed() {
   DCHECK(item_weak_);
   item_weak_->RemoveObserver(this);
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 4d3d400..58f4a82 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -340,6 +340,7 @@
   void ItemNameChanged() override;
   void ItemBadgeVisibilityChanged() override;
   void ItemBadgeColorChanged() override;
+  void ItemIsNewInstallChanged() override;
   void ItemBeingDestroyed() override;
 
   // ui::ImplicitAnimationObserver:
diff --git a/ash/app_list/views/continue_section_view_unittest.cc b/ash/app_list/views/continue_section_view_unittest.cc
index ef3e23a..0d4381f 100644
--- a/ash/app_list/views/continue_section_view_unittest.cc
+++ b/ash/app_list/views/continue_section_view_unittest.cc
@@ -434,6 +434,33 @@
   EXPECT_EQ("id1", client->last_opened_search_result());
 }
 
+TEST_P(ContinueSectionViewTest, RemoveWithContextMenuOption) {
+  AddSearchResult("id1", AppListSearchResultType::kFileChip);
+  AddSearchResult("id2", AppListSearchResultType::kDriveChip);
+  AddSearchResult("id3", AppListSearchResultType::kDriveChip);
+
+  EnsureLauncherShown();
+
+  VerifyResultViewsUpdated();
+
+  ContinueTaskView* continue_task_view = GetResultViewAt(0);
+  EXPECT_EQ(continue_task_view->result()->id(), "id1");
+
+  GetContinueSectionView()->GetWidget()->LayoutRootViewIfNecessary();
+  SimulateRightClickOrLongPressAt(
+      continue_task_view->GetBoundsInScreen().CenterPoint());
+  EXPECT_TRUE(continue_task_view->IsMenuShowing());
+  continue_task_view->ExecuteCommand(ContinueTaskCommandId::kRemoveResult,
+                                     ui::EventFlags::EF_NONE);
+
+  TestAppListClient* client = GetAppListTestHelper()->app_list_client();
+  std::vector<TestAppListClient::SearchResultActionId> expected_actions = {
+      {"id1", SearchResultActionType::kRemove}};
+  std::vector<TestAppListClient::SearchResultActionId> invoked_actions =
+      client->GetAndClearInvokedResultActions();
+  EXPECT_EQ(expected_actions, invoked_actions);
+}
+
 TEST_P(ContinueSectionViewTest, ResultRemovedContextMenuCloses) {
   AddSearchResult("id1", AppListSearchResultType::kFileChip);
   AddSearchResult("id2", AppListSearchResultType::kDriveChip);
diff --git a/ash/app_list/views/continue_task_view.cc b/ash/app_list/views/continue_task_view.cc
index 6995f641..8f4246b 100644
--- a/ash/app_list/views/continue_task_view.cc
+++ b/ash/app_list/views/continue_task_view.cc
@@ -234,7 +234,7 @@
       OpenResult(event_flags);
       break;
     case ContinueTaskCommandId::kRemoveResult:
-      // TODO(anasalar): Implement Remove Suggestion.
+      RemoveResult();
       break;
     default:
       NOTREACHED();
@@ -249,7 +249,11 @@
           IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_OPEN),
       ui::ImageModel::FromVectorIcon(kLaunchIcon));
 
-  // TODO(crbug.com/1264530): Add context menu option for removing a suggestion.
+  context_menu_model_->AddItemWithIcon(
+      ContinueTaskCommandId::kRemoveResult,
+      l10n_util::GetStringUTF16(
+          IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_REMOVE),
+      ui::ImageModel::FromVectorIcon(kRemoveOutlineIcon));
 
   return context_menu_model_.get();
 }
@@ -263,6 +267,14 @@
       false /* launch_as_default */);
 }
 
+void ContinueTaskView::RemoveResult() {
+  // TODO(crbug.com/1264530): The ML service may change the way Search Results
+  // are removed.
+  DCHECK(result());
+  view_delegate_->InvokeSearchResultAction(result()->id(),
+                                           SearchResultActionType::kRemove);
+}
+
 bool ContinueTaskView::IsMenuShowing() const {
   return context_menu_runner_ && context_menu_runner_->IsRunning();
 }
diff --git a/ash/app_list/views/continue_task_view.h b/ash/app_list/views/continue_task_view.h
index 7f3729e0..749255f 100644
--- a/ash/app_list/views/continue_task_view.h
+++ b/ash/app_list/views/continue_task_view.h
@@ -80,6 +80,9 @@
   // Opens the search result related to the view.
   void OpenResult(int event_flags);
 
+  // Removes the search result related to the view.
+  void RemoveResult();
+
   // Builds and returns a raw pointer to `context_menu_model_`.
   ui::SimpleMenuModel* BuildMenuModel();
 
diff --git a/ash/components/arc/metrics/stability_metrics_manager.cc b/ash/components/arc/metrics/stability_metrics_manager.cc
index 5e7d9167..c0bb20f 100644
--- a/ash/components/arc/metrics/stability_metrics_manager.cc
+++ b/ash/components/arc/metrics/stability_metrics_manager.cc
@@ -71,7 +71,7 @@
 void StabilityMetricsManager::ResetMetrics() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DictionaryPrefUpdate update(local_state_, prefs::kStabilityMetrics);
-  update->Clear();
+  update->DictClear();
 }
 
 absl::optional<bool> StabilityMetricsManager::GetArcEnabledState() {
diff --git a/ash/components/arc/video_accelerator/BUILD.gn b/ash/components/arc/video_accelerator/BUILD.gn
index c5a806ac..5b32235 100644
--- a/ash/components/arc/video_accelerator/BUILD.gn
+++ b/ash/components/arc/video_accelerator/BUILD.gn
@@ -18,6 +18,8 @@
       "gpu_arc_video_encode_accelerator.h",
       "gpu_arc_video_protected_buffer_allocator.cc",
       "gpu_arc_video_protected_buffer_allocator.h",
+      "oop_arc_video_accelerator_factory.cc",
+      "oop_arc_video_accelerator_factory.h",
       "protected_buffer_manager_proxy.cc",
       "protected_buffer_manager_proxy.h",
     ]
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index 7d87a7b..3158416 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -99,6 +99,13 @@
   base::UmaHistogramExactLinear(
       "Media.GpuArcVideoDecodeAccelerator.InstanceCount.All", instance_count_,
       /*exclusive_max=*/50);
+
+  // If out-of-process video decoding is enabled, there should only be one
+  // GpuArcVideoDecodeAccelerator instance per process. This is enforced in the
+  // browser process by the
+  // arc::<unnamed namespace>::VideoAcceleratorFactoryService.
+  DCHECK(!base::FeatureList::IsEnabled(arc::kOutOfProcessVideoDecoding) ||
+         instance_count_ == 1);
 }
 
 GpuArcVideoDecodeAccelerator::~GpuArcVideoDecodeAccelerator() {
diff --git a/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.cc b/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.cc
new file mode 100644
index 0000000..345e113
--- /dev/null
+++ b/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.cc
@@ -0,0 +1,67 @@
+// 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 "ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.h"
+
+#include "ash/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.h"
+#include "ash/components/arc/video_accelerator/protected_buffer_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/config/gpu_preferences.h"
+#include "media/gpu/macros.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+namespace arc {
+
+// TODO(b/195769334): we should plumb meaningful gpu::GpuPreferences and
+// gpu::GpuDriverBugWorkarounds so that we can use them to control behaviors of
+// the hardware decoder.
+// TODO(b/195769334): plumb a ProtectedBufferManager.
+OOPArcVideoAcceleratorFactory::OOPArcVideoAcceleratorFactory(
+    mojo::PendingReceiver<mojom::VideoAcceleratorFactory> receiver)
+    : receiver_(this, std::move(receiver)) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+OOPArcVideoAcceleratorFactory::~OOPArcVideoAcceleratorFactory() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void OOPArcVideoAcceleratorFactory::CreateDecodeAccelerator(
+    mojo::PendingReceiver<mojom::VideoDecodeAccelerator> receiver) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  VLOGF(2);
+  // Note that a well-behaved client should not reach this point twice because
+  // there should be one process for each GpuArcVideoDecodeAccelerator. This is
+  // guaranteed by arc::GpuArcVideoServiceHost in the browser process. Thus, we
+  // don't bother validating that here because if the browser process is
+  // compromised, we have bigger problems.
+  // TODO(b/195769334): plumb a ProtectedBufferManager.
+  auto decoder = std::make_unique<GpuArcVideoDecodeAccelerator>(
+      gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds(),
+      /*protected_buffer_manager=*/nullptr);
+  auto decoder_receiver =
+      mojo::MakeSelfOwnedReceiver(std::move(decoder), std::move(receiver));
+  CHECK(decoder_receiver);
+  decoder_receiver->set_connection_error_handler(
+      base::BindOnce(&OOPArcVideoAcceleratorFactory::OnDecoderDisconnected,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OOPArcVideoAcceleratorFactory::CreateEncodeAccelerator(
+    mojo::PendingReceiver<mojom::VideoEncodeAccelerator> receiver) {
+  NOTIMPLEMENTED();
+}
+
+void OOPArcVideoAcceleratorFactory::CreateProtectedBufferAllocator(
+    mojo::PendingReceiver<mojom::VideoProtectedBufferAllocator> receiver) {
+  NOTREACHED();
+}
+
+void OOPArcVideoAcceleratorFactory::OnDecoderDisconnected() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  VLOGF(2);
+  receiver_.reset();
+}
+
+}  // namespace arc
diff --git a/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.h b/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.h
new file mode 100644
index 0000000..f17b2ad3
--- /dev/null
+++ b/ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_OOP_ARC_VIDEO_ACCELERATOR_FACTORY_H_
+#define ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_OOP_ARC_VIDEO_ACCELERATOR_FACTORY_H_
+
+#include "ash/components/arc/mojom/video.mojom.h"
+#include "ash/components/arc/mojom/video_decode_accelerator.mojom.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace arc {
+
+class GpuArcVideoDecodeAccelerator;
+
+// An OOPArcVideoAcceleratorFactory runs in its own process and wraps a single
+// GpuArcVideoDecodeAccelerator. When the GpuArcVideoDecodeAccelerator gets
+// disconnected, OOPArcVideoAcceleratorFactory disconnects its own |receiver_|
+// (this is intended to signal the mojo::ServiceFactory that it can destroy the
+// OOPArcVideoAcceleratorFactory which in turn should trigger the end of the
+// process).
+class OOPArcVideoAcceleratorFactory
+    : public arc::mojom::VideoAcceleratorFactory {
+ public:
+  OOPArcVideoAcceleratorFactory(
+      mojo::PendingReceiver<mojom::VideoAcceleratorFactory> receiver);
+  OOPArcVideoAcceleratorFactory(const OOPArcVideoAcceleratorFactory&) = delete;
+  OOPArcVideoAcceleratorFactory& operator=(
+      const OOPArcVideoAcceleratorFactory&) = delete;
+  ~OOPArcVideoAcceleratorFactory() override;
+
+  // arc::mojom:::VideoAcceleratorFactory implementation.
+  void CreateDecodeAccelerator(
+      mojo::PendingReceiver<mojom::VideoDecodeAccelerator> receiver) override;
+  void CreateEncodeAccelerator(
+      mojo::PendingReceiver<mojom::VideoEncodeAccelerator> receiver) override;
+  void CreateProtectedBufferAllocator(
+      mojo::PendingReceiver<mojom::VideoProtectedBufferAllocator> receiver)
+      override;
+
+ private:
+  void OnDecoderDisconnected();
+
+  mojo::Receiver<mojom::VideoAcceleratorFactory> receiver_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<OOPArcVideoAcceleratorFactory> weak_ptr_factory_{this};
+};
+
+}  // namespace arc
+
+#endif  // ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_OOP_ARC_VIDEO_ACCELERATOR_FACTORY_H_
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.cc b/ash/components/audio/audio_devices_pref_handler_impl.cc
index 2217ede..4048b85 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -310,7 +310,7 @@
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesMutePref() {
   DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesMute);
-  dict_update->Clear();
+  dict_update->DictClear();
   dict_update->MergeDictionary(device_mute_settings_.get());
 }
 
@@ -324,7 +324,7 @@
 void AudioDevicesPrefHandlerImpl::SaveDevicesVolumePref() {
   DictionaryPrefUpdate dict_update(local_state_,
                                    prefs::kAudioDevicesVolumePercent);
-  dict_update->Clear();
+  dict_update->DictClear();
   dict_update->MergeDictionary(device_volume_settings_.get());
 }
 
@@ -338,7 +338,7 @@
 void AudioDevicesPrefHandlerImpl::SaveDevicesGainPref() {
   DictionaryPrefUpdate dict_update(local_state_,
                                    prefs::kAudioDevicesGainPercent);
-  dict_update->Clear();
+  dict_update->DictClear();
   dict_update->MergeDictionary(device_gain_settings_.get());
 }
 
@@ -351,7 +351,7 @@
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesStatePref() {
   DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesState);
-  dict_update->Clear();
+  dict_update->DictClear();
   dict_update->MergeDictionary(device_state_settings_.get());
 }
 
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index c18d5a3..d007b28b 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -639,7 +639,7 @@
 
   DictionaryPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations);
   base::DictionaryValue* pref_data = update.Get();
-  pref_data->Clear();
+  pref_data->DictClear();
 
   const display::TouchDeviceManager::TouchAssociationMap& touch_associations =
       touch_device_manager->touch_associations();
@@ -690,7 +690,7 @@
   DictionaryPrefUpdate update_port(pref_service,
                                    prefs::kDisplayTouchPortAssociations);
   pref_data = update_port.Get();
-  update_port->Clear();
+  update_port->DictClear();
 
   const display::TouchDeviceManager::PortAssociationMap& port_associations =
       touch_device_manager->port_associations();
@@ -730,7 +730,7 @@
   DictionaryPrefUpdate update(pref_service,
                               prefs::kDisplayMixedMirrorModeParams);
   base::DictionaryValue* pref_data = update.Get();
-  pref_data->Clear();
+  pref_data->DictClear();
 
   if (!mixed_params)
     return;
diff --git a/ash/drag_drop/drag_drop_capture_delegate.cc b/ash/drag_drop/drag_drop_capture_delegate.cc
index 364473b8495..24f1f3b 100644
--- a/ash/drag_drop/drag_drop_capture_delegate.cc
+++ b/ash/drag_drop/drag_drop_capture_delegate.cc
@@ -57,7 +57,7 @@
   return drag_drop_tracker_->GetTarget(event);
 }
 
-ui::LocatedEvent* DragDropCaptureDelegate::ConvertEvent(
+std::unique_ptr<ui::LocatedEvent> DragDropCaptureDelegate::ConvertEvent(
     aura::Window* target,
     const ui::LocatedEvent& event) {
   return drag_drop_tracker_->ConvertEvent(target, event);
diff --git a/ash/drag_drop/drag_drop_capture_delegate.h b/ash/drag_drop/drag_drop_capture_delegate.h
index 2af06559..44c197e 100644
--- a/ash/drag_drop/drag_drop_capture_delegate.h
+++ b/ash/drag_drop/drag_drop_capture_delegate.h
@@ -51,8 +51,8 @@
   //
   // This should only be called on events if TakeCapture returned true at the
   // start of a drag and drop session.
-  ui::LocatedEvent* ConvertEvent(aura::Window* target,
-                                 const ui::LocatedEvent& event);
+  std::unique_ptr<ui::LocatedEvent> ConvertEvent(aura::Window* target,
+                                                 const ui::LocatedEvent& event);
 
   // Return the capture window used if TakeCapture returns true.
   aura::Window* capture_window();
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 14e19d2..b55f11e 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -417,13 +417,12 @@
     return;
   }
 
-  ui::LocatedEvent* translated_event;
+  std::unique_ptr<ui::LocatedEvent> translated_event;
   if (capture_delegate_) {
     translated_event =
         capture_delegate_->ConvertEvent(translated_target, touch_offset_event);
   } else {
-    translated_event =
-        ConvertEvent(translated_target, touch_offset_event).release();
+    translated_event = ConvertEvent(translated_target, touch_offset_event);
   }
 
   switch (event->type()) {
diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc
index 94427e21..54d97192 100644
--- a/ash/drag_drop/drag_drop_tracker.cc
+++ b/ash/drag_drop/drag_drop_tracker.cc
@@ -105,7 +105,8 @@
 DragDropTracker::DragDropTracker(aura::Window* context_root,
                                  CancelDragDropCallback callback)
     : tracker_window_delegate_(new DragDropTrackerDelegate(callback)) {
-  capture_window_ = CreateCaptureWindow(context_root, tracker_window_delegate_);
+  capture_window_ =
+      CreateCaptureWindow(context_root, tracker_window_delegate_.get());
 }
 
 DragDropTracker::~DragDropTracker() {
@@ -127,8 +128,9 @@
   return root_window_at_point->GetEventHandlerForPoint(location_in_root);
 }
 
-ui::LocatedEvent* DragDropTracker::ConvertEvent(aura::Window* target,
-                                                const ui::LocatedEvent& event) {
+std::unique_ptr<ui::LocatedEvent> DragDropTracker::ConvertEvent(
+    aura::Window* target,
+    const ui::LocatedEvent& event) {
   DCHECK(capture_window_.get());
   gfx::Point target_location = event.location();
   aura::Window::ConvertPointToTarget(capture_window_.get(), target,
@@ -142,9 +144,9 @@
   int changed_button_flags = 0;
   if (event.IsMouseEvent())
     changed_button_flags = event.AsMouseEvent()->changed_button_flags();
-  return new ui::MouseEvent(event.type(), target_location, target_root_location,
-                            ui::EventTimeForNow(), event.flags(),
-                            changed_button_flags);
+  return std::make_unique<ui::MouseEvent>(
+      event.type(), target_location, target_root_location,
+      ui::EventTimeForNow(), event.flags(), changed_button_flags);
 }
 
 }  // namespace ash
diff --git a/ash/drag_drop/drag_drop_tracker.h b/ash/drag_drop/drag_drop_tracker.h
index 65f3c8e..1b21676 100644
--- a/ash/drag_drop/drag_drop_tracker.h
+++ b/ash/drag_drop/drag_drop_tracker.h
@@ -46,14 +46,13 @@
 
   // Converts the locations of |event| in the coordinates of the active root
   // window to the ones in |target|'s coordinates.
-  // Caller takes ownership of the returned object.
-  ui::LocatedEvent* ConvertEvent(aura::Window* target,
-                                 const ui::LocatedEvent& event);
+  std::unique_ptr<ui::LocatedEvent> ConvertEvent(aura::Window* target,
+                                                 const ui::LocatedEvent& event);
 
  private:
+  std::unique_ptr<ash::DragDropTrackerDelegate> tracker_window_delegate_;
   // A window for capturing drag events while dragging.
   std::unique_ptr<aura::Window> capture_window_;
-  ash::DragDropTrackerDelegate* tracker_window_delegate_;
 };
 
 }  // namespace ash
diff --git a/ash/drag_drop/drag_drop_tracker_unittest.cc b/ash/drag_drop/drag_drop_tracker_unittest.cc
index c8ed1b4..14ccad2 100644
--- a/ash/drag_drop/drag_drop_tracker_unittest.cc
+++ b/ash/drag_drop/drag_drop_tracker_unittest.cc
@@ -38,12 +38,12 @@
     return target;
   }
 
-  static ui::LocatedEvent* ConvertEvent(aura::Window* target,
-                                        const ui::MouseEvent& event) {
+  static std::unique_ptr<ui::LocatedEvent> ConvertEvent(
+      aura::Window* target,
+      const ui::MouseEvent& event) {
     std::unique_ptr<DragDropTracker> tracker(new DragDropTracker(
         Shell::GetPrimaryRootWindow(), base::BindLambdaForTesting([&]() {})));
-    ui::LocatedEvent* converted = tracker->ConvertEvent(target, event);
-    return converted;
+    return tracker->ConvertEvent(target, event);
   }
 };
 
diff --git a/ash/login/ui/login_auth_factors_view_unittest.cc b/ash/login/ui/login_auth_factors_view_unittest.cc
index a8d9de1e..ea64b7b 100644
--- a/ash/login/ui/login_auth_factors_view_unittest.cc
+++ b/ash/login/ui/login_auth_factors_view_unittest.cc
@@ -127,17 +127,23 @@
   void SetUp() override {
     AshTestBase::SetUp();
 
-    view_ = new LoginAuthFactorsView(base::BindRepeating(
-        &LoginAuthFactorsViewUnittest::set_click_to_enter_called,
-        base::Unretained(this), true));
-
     // We proxy |view_| inside of |container_| so we can control layout.
     // TODO(crbug.com/1233614): Add layout tests to check positioning/ordering
     // of icons.
-    container_ = new views::View();
+    container_ = std::make_unique<views::View>();
     container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
         views::BoxLayout::Orientation::kVertical));
-    container_->AddChildView(view_);
+
+    view_ = container_->AddChildView(
+        std::make_unique<LoginAuthFactorsView>(base::BindRepeating(
+            &LoginAuthFactorsViewUnittest::set_click_to_enter_called,
+            base::Unretained(this), true)));
+  }
+
+  void TearDown() override {
+    container_.reset();
+    view_ = nullptr;
+    AshTestBase::TearDown();
   }
 
   void AddAuthFactors(std::vector<AuthFactorType> types) {
@@ -164,9 +170,8 @@
   }
 
   base::test::ScopedFeatureList feature_list_;
-  views::View* container_ = nullptr;  // Owned by test widget view hierarchy.
-  LoginAuthFactorsView* view_ =
-      nullptr;  // Owned by test widget view hierarchy.
+  std::unique_ptr<views::View> container_;
+  LoginAuthFactorsView* view_ = nullptr;  // Owned by container.
   std::vector<FakeAuthFactorModel*> auth_factors_;
   bool click_to_enter_called_ = false;
 };
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc
index b7a02b79..99e3d6d 100644
--- a/ash/public/cpp/app_list/app_list_types.cc
+++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -118,7 +118,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // SearchResultAction:
 
-SearchResultAction::SearchResultAction() {}
+SearchResultAction::SearchResultAction() = default;
 
 SearchResultAction::SearchResultAction(SearchResultActionType type,
                                        const gfx::ImageSkia& image,
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index dbd0799..d61fd3bd 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -124,6 +124,9 @@
   bool is_page_break = false;  // Whether this item is a "page break" item.
   SkColor badge_color = SK_ColorWHITE;  // Notification badge color.
 
+  // Whether the app was installed this session and has not yet been launched.
+  bool is_new_install = false;
+
   int icon_version = 0;  // An int represent icon version. If changed, `icon`
                          // should be reloaded.
 
diff --git a/ash/services/ime/ime_decoder.cc b/ash/services/ime/ime_decoder.cc
index 9456a1e..777f078 100644
--- a/ash/services/ime/ime_decoder.cc
+++ b/ash/services/ime/ime_decoder.cc
@@ -68,12 +68,6 @@
 
   base::FilePath path = GetImeDecoderLibPath();
 
-  if (!base::PathExists(path)) {
-    LOG(WARNING) << "IME decoder shared library is not installed.";
-    status_ = Status::kNotInstalled;
-    return;
-  }
-
   // Add dlopen flags (RTLD_LAZY | RTLD_NODELETE) later.
   base::ScopedNativeLibrary library = base::ScopedNativeLibrary(path);
   if (!library.is_valid()) {
diff --git a/ash/shelf/contextual_tooltip.cc b/ash/shelf/contextual_tooltip.cc
index 8d8893b..ea716d90 100644
--- a/ash/shelf/contextual_tooltip.cc
+++ b/ash/shelf/contextual_tooltip.cc
@@ -261,7 +261,7 @@
       prefs::kContextualTooltips);
   base::DictionaryValue* nudges_dict = update.Get();
   if (nudges_dict && !nudges_dict->DictEmpty())
-    nudges_dict->Clear();
+    nudges_dict->DictClear();
 }
 
 void OverrideClockForTesting(base::Clock* test_clock) {
diff --git a/ash/system/bluetooth/bluetooth_device_list_item_multiple_battery_view.cc b/ash/system/bluetooth/bluetooth_device_list_item_multiple_battery_view.cc
index 58f36fd..69321fbd 100644
--- a/ash/system/bluetooth/bluetooth_device_list_item_multiple_battery_view.cc
+++ b/ash/system/bluetooth/bluetooth_device_list_item_multiple_battery_view.cc
@@ -50,7 +50,7 @@
         battery_info->left_bud_info->battery_percentage,
         IDS_ASH_STATUS_TRAY_BLUETOOTH_DEVICE_BATTERY_PERCENTAGE_LEFT_BUD_LABEL);
   } else if (left_bud_battery_view_) {
-    RemoveChildView(left_bud_battery_view_);
+    RemoveChildViewT(left_bud_battery_view_);
     left_bud_battery_view_ = nullptr;
   }
 
@@ -65,7 +65,7 @@
         battery_info->case_info->battery_percentage,
         IDS_ASH_STATUS_TRAY_BLUETOOTH_DEVICE_BATTERY_PERCENTAGE_CASE_LABEL);
   } else if (case_battery_view_) {
-    RemoveChildView(case_battery_view_);
+    RemoveChildViewT(case_battery_view_);
     case_battery_view_ = nullptr;
   }
 
@@ -80,7 +80,7 @@
         battery_info->right_bud_info->battery_percentage,
         IDS_ASH_STATUS_TRAY_BLUETOOTH_DEVICE_BATTERY_PERCENTAGE_RIGHT_BUD_LABEL);
   } else if (right_bud_battery_view_) {
-    RemoveChildView(right_bud_battery_view_);
+    RemoveChildViewT(right_bud_battery_view_);
     right_bud_battery_view_ = nullptr;
   }
 }
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc
index 9dddffe..feb9cc03 100644
--- a/ash/system/message_center/ash_notification_view.cc
+++ b/ash/system/message_center/ash_notification_view.cc
@@ -28,6 +28,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/font.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -103,10 +104,14 @@
 
 // Configure the style for labels in notification view. `is_color_primary`
 // indicates if the color of the text is primary or secondary text color.
-void ConfigureLabelStyle(views::Label* label, int size, bool is_color_primary) {
+void ConfigureLabelStyle(
+    views::Label* label,
+    int size,
+    bool is_color_primary,
+    gfx::Font::Weight font_weight = gfx::Font::Weight::NORMAL) {
   label->SetAutoColorReadabilityEnabled(false);
-  label->SetFontList(gfx::FontList({kGoogleSansFont}, gfx::Font::NORMAL, size,
-                                   gfx::Font::Weight::MEDIUM));
+  label->SetFontList(
+      gfx::FontList({kGoogleSansFont}, gfx::Font::NORMAL, size, font_weight));
   auto layer_type =
       is_color_primary
           ? ash::AshColorProvider::ContentLayerType::kTextColorPrimary
@@ -189,7 +194,7 @@
                       /*is_color_primary=*/false);
   message_center_utils::InitLayerForAnimations(timestamp_in_collapsed_view_);
   ConfigureLabelStyle(title_view_, kTitleLabelSize,
-                      /*is_color_primary=*/true);
+                      /*is_color_primary=*/true, gfx::Font::Weight::MEDIUM);
 }
 
 AshNotificationView::NotificationTitleRow::~NotificationTitleRow() {
@@ -289,6 +294,7 @@
                   .AddChild(CreateRightContentBuilder())
                   .AddChild(
                       views::Builder<views::FlexLayoutView>()
+                          .CopyAddressTo(&expand_button_container_)
                           .SetOrientation(views::LayoutOrientation::kHorizontal)
                           .AddChild(
                               views::Builder<AshNotificationExpandButton>()
@@ -298,9 +304,7 @@
                                       base::Unretained(this)))
                                   .SetProperty(
                                       views::kCrossAxisAlignmentKey,
-                                      IsExpanded()
-                                          ? views::LayoutAlignment::kStart
-                                          : views::LayoutAlignment::kCenter))));
+                                      views::LayoutAlignment::kStart))));
 
   // Main right view contains all the views besides control buttons and
   // icon.
@@ -574,9 +578,6 @@
           expanded ? kControlButtonsContainerExpandedPadding
                    : kControlButtonsContainerCollapsedPadding);
 
-  app_icon_view_->SetBorder(views::CreateEmptyBorder(
-      expanded ? kAppIconViewExpandedPadding : kAppIconViewCollapsedPadding));
-
   bool is_single_expanded_notification =
       !is_grouped_child_view_ && !is_grouped_parent_view_ && expanded;
   header_row()->SetVisible(is_grouped_parent_view_ ||
@@ -598,9 +599,15 @@
                                                 !is_grouped_parent_view_);
   }
 
-  expand_button_->SetProperty(views::kCrossAxisAlignmentKey,
-                              expanded ? views::LayoutAlignment::kStart
-                                       : views::LayoutAlignment::kCenter);
+  // Custom padding for app icon and expand button. These 2 views should always
+  // use the same padding value so that they are vertical aligned.
+  app_icon_view_->SetBorder(views::CreateEmptyBorder(
+      expanded ? kAppIconExpandButtonExpandedPadding
+               : kAppIconExpandButtonCollapsedPadding));
+  expand_button_container_->SetInteriorMargin(
+      expanded ? kAppIconExpandButtonExpandedPadding
+               : kAppIconExpandButtonCollapsedPadding);
+
   expand_button_->SetExpanded(expanded);
 
   static_cast<views::BoxLayout*>(
diff --git a/ash/system/message_center/ash_notification_view.h b/ash/system/message_center/ash_notification_view.h
index f277777..fa0d5243 100644
--- a/ash/system/message_center/ash_notification_view.h
+++ b/ash/system/message_center/ash_notification_view.h
@@ -18,6 +18,7 @@
 
 namespace views {
 class BoxLayout;
+class FlexLayoutView;
 class ImageButton;
 class LabelButton;
 class View;
@@ -162,6 +163,7 @@
   // Owned by views hierarchy.
   RoundedImageView* app_icon_view_ = nullptr;
   AshNotificationExpandButton* expand_button_ = nullptr;
+  views::FlexLayoutView* expand_button_container_ = nullptr;
   views::View* control_buttons_container_ = nullptr;
   views::View* left_content_ = nullptr;
   views::Label* message_view_in_expanded_state_ = nullptr;
diff --git a/ash/system/message_center/ash_notification_view_unittest.cc b/ash/system/message_center/ash_notification_view_unittest.cc
index ac5a90e..a35d39b5 100644
--- a/ash/system/message_center/ash_notification_view_unittest.cc
+++ b/ash/system/message_center/ash_notification_view_unittest.cc
@@ -20,6 +20,7 @@
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/test/button_test_api.h"
 
 using message_center::Notification;
@@ -176,6 +177,9 @@
   AshNotificationExpandButton* expand_button() {
     return notification_view_->expand_button_;
   }
+  views::FlexLayoutView* expand_button_container() {
+    return notification_view_->expand_button_container_;
+  }
   views::View* inline_settings_row() {
     return notification_view()->inline_settings_row();
   }
@@ -490,4 +494,28 @@
   EXPECT_TRUE(snooze_button()->GetVisible());
 }
 
+TEST_F(AshNotificationViewTest, AppIconAndExpandButtonAlignment) {
+  auto notification = CreateTestNotification();
+  notification_view()->UpdateWithNotification(*notification);
+
+  // Make sure that app icon and expand button is vertically aligned in
+  // collapsed mode. Also, the padding of them should be the same.
+  notification_view()->SetExpanded(false);
+  EXPECT_EQ(app_icon_view()->GetBoundsInScreen().y(),
+            expand_button_container()->GetBoundsInScreen().y());
+  EXPECT_EQ(app_icon_view()->GetContentsBounds().y(),
+            expand_button_container()->GetInteriorMargin().top());
+
+  // Make sure that app icon, expand button, and also header row is vertically
+  // aligned in collapsed mode. Also check the padding for app icon and expand
+  // button again.
+  notification_view()->SetExpanded(true);
+  EXPECT_EQ(app_icon_view()->GetBoundsInScreen().y(),
+            expand_button_container()->GetBoundsInScreen().y());
+  EXPECT_EQ(app_icon_view()->GetBoundsInScreen().y(),
+            header_row()->GetBoundsInScreen().y());
+  EXPECT_EQ(app_icon_view()->GetContentsBounds().y(),
+            expand_button_container()->GetInteriorMargin().top());
+}
+
 }  // namespace ash
diff --git a/ash/system/message_center/message_center_constants.h b/ash/system/message_center/message_center_constants.h
index 596bc091..72c19a78 100644
--- a/ash/system/message_center/message_center_constants.h
+++ b/ash/system/message_center/message_center_constants.h
@@ -28,7 +28,7 @@
 constexpr int kMessageListNotificationSpacing = 2;
 
 constexpr int kNotificationBarVerticalPadding = 8;
-constexpr int kNotificationBarHorizontalPadding = 16;
+constexpr int kNotificationBarHorizontalPadding = 10;
 
 // Constants for `ash_notification_view`.
 
@@ -44,8 +44,8 @@
 constexpr gfx::Size kNotificationExpandButtonSize(24, 24);
 constexpr gfx::Size kNotificationExpandButtonWithLabelSize(40, 24);
 
-constexpr gfx::Insets kAppIconViewExpandedPadding(2, 0, 0, 0);
-constexpr gfx::Insets kAppIconViewCollapsedPadding(6, 0, 0, 0);
+constexpr gfx::Insets kAppIconExpandButtonExpandedPadding(2, 0, 0, 0);
+constexpr gfx::Insets kAppIconExpandButtonCollapsedPadding(12, 0, 0, 0);
 
 constexpr gfx::Insets kControlButtonsContainerExpandedPadding(6, 0, 2, 0);
 constexpr gfx::Insets kControlButtonsContainerCollapsedPadding(2, 0, 0, 0);
diff --git a/ash/system/power/power_prefs.cc b/ash/system/power/power_prefs.cc
index 80a60045..f0bc16c 100644
--- a/ash/system/power/power_prefs.cc
+++ b/ash/system/power/power_prefs.cc
@@ -11,6 +11,7 @@
 #include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
+#include "ash/system/hps/hps_configuration.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/time/default_tick_clock.h"
@@ -104,6 +105,7 @@
                                 true);
   registry->RegisterBooleanPref(prefs::kPowerSmartDimEnabled, true);
   registry->RegisterBooleanPref(prefs::kPowerAlsLoggingEnabled, false);
+  registry->RegisterBooleanPref(prefs::kPowerQuickDimEnabled, false);
 
   registry->RegisterBooleanPref(prefs::kAllowScreenLock, true);
   registry->RegisterBooleanPref(
@@ -285,6 +287,13 @@
         prefs->GetDouble(prefs::kPowerUserActivityScreenDimDelayFactor);
   }
 
+  if (prefs->GetBoolean(prefs::kPowerQuickDimEnabled)) {
+    values.battery_quick_dim_delay_ms =
+        ash::GetQuickDimDelay().InMilliseconds();
+    values.ac_quick_dim_delay_ms = ash::GetQuickDimDelay().InMilliseconds();
+    values.send_feedback_if_undimmed = ash::GetQuickDimFeedbackEnabled();
+  }
+
   values.wait_for_initial_user_activity =
       prefs->GetBoolean(prefs::kPowerWaitForInitialUserActivity);
   values.force_nonzero_brightness_for_user_activity =
@@ -412,6 +421,7 @@
   profile_registrar_->Add(prefs::kPowerFastSuspendWhenBacklightsForcedOff,
                           update_callback);
   profile_registrar_->Add(prefs::kPowerAlsLoggingEnabled, update_callback);
+  profile_registrar_->Add(prefs::kPowerQuickDimEnabled, update_callback);
 
   UpdatePowerPolicyFromPrefs();
 }
diff --git a/ash/system/power/power_prefs_unittest.cc b/ash/system/power/power_prefs_unittest.cc
index 84beb13..8840d947 100644
--- a/ash/system/power/power_prefs_unittest.cc
+++ b/ash/system/power/power_prefs_unittest.cc
@@ -14,6 +14,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/session/test_session_controller_client.h"
 #include "ash/shell.h"
+#include "ash/system/hps/hps_configuration.h"
 #include "ash/test/ash_test_base.h"
 #include "base/callback_helpers.h"
 #include "base/json/json_reader.h"
@@ -534,4 +535,19 @@
   EXPECT_FALSE(prefs->GetBoolean(prefs::kPowerAlsLoggingEnabled));
 }
 
+TEST_F(PowerPrefsTest, SetQuickDimParams) {
+  PrefService* prefs =
+      Shell::Get()->session_controller()->GetActivePrefService();
+  // This will trigger UpdatePowerPolicyFromPrefs and set correct parameters.
+  prefs->SetBoolean(prefs::kPowerQuickDimEnabled, true);
+
+  const auto policy = power_manager_client()->policy();
+  EXPECT_EQ(policy.ac_delays().quick_dim_ms(),
+            ash::GetQuickDimDelay().InMilliseconds());
+  EXPECT_EQ(policy.battery_delays().quick_dim_ms(),
+            ash::GetQuickDimDelay().InMilliseconds());
+  EXPECT_EQ(policy.send_feedback_if_undimmed(),
+            ash::GetQuickDimFeedbackEnabled());
+}
+
 }  // namespace ash
diff --git a/ash/system/time/calendar_month_view.cc b/ash/system/time/calendar_month_view.cc
index 6330e1e..9f5d95d 100644
--- a/ash/system/time/calendar_month_view.cc
+++ b/ash/system/time/calendar_month_view.cc
@@ -274,6 +274,8 @@
     MoveToNextDay(column, current_date, current_date_exploded);
   }
 
+  last_row_index_ = row_number - 1;
+
   // TODO(https://crbug.com/1236276): Handle some cases when the first day is
   // not Sunday.
   if (current_date_exploded.day_of_week == 0)
diff --git a/ash/system/time/calendar_month_view.h b/ash/system/time/calendar_month_view.h
index 6aadaf55..a784a15 100644
--- a/ash/system/time/calendar_month_view.h
+++ b/ash/system/time/calendar_month_view.h
@@ -99,6 +99,9 @@
   // If today's cell is in this view.
   bool has_today() { return has_today_; }
 
+  // Returns the index of this month view's last row.
+  int last_row_index() const { return last_row_index_; }
+
  private:
   // Adds the `current_date`'s `CalendarDateCellView` to the table layout and
   // returns it.
@@ -114,6 +117,9 @@
   // If today's cell is in this view.
   bool has_today_ = false;
 
+  // The index of this month view's last row.
+  int last_row_index_;
+
   // The cells of each row that should be first focused on. These
   // `CalendarDateCellView`s are the children of this view.
   std::vector<CalendarDateCellView*> focused_cells_;
diff --git a/ash/system/time/calendar_unittest_utils.h b/ash/system/time/calendar_unittest_utils.h
index ec7f405c..538e155 100644
--- a/ash/system/time/calendar_unittest_utils.h
+++ b/ash/system/time/calendar_unittest_utils.h
@@ -14,6 +14,10 @@
 
 namespace calendar_test_utils {
 
+// A duration to let the animation finish and pass the cool down duration in
+// tests.
+constexpr base::TimeDelta kAnimationSettleDownDuration = base::Seconds(3);
+
 // Creates a `google_apis::calendar::CalendarEvent` for testing.
 std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
     const char* id,
diff --git a/ash/system/time/calendar_utils.h b/ash/system/time/calendar_utils.h
index ca5b638..5e15363 100644
--- a/ash/system/time/calendar_utils.h
+++ b/ash/system/time/calendar_utils.h
@@ -32,6 +32,13 @@
 constexpr gfx::Insets kDateCellInsets{kDateVerticalPadding,
                                       kDateHorizontalPadding};
 
+// Duration of opacity animation for visibility changes.
+constexpr base::TimeDelta kAnimationDurationForVisibility =
+    base::Milliseconds(100);
+
+// Duration of moving animation.
+constexpr base::TimeDelta kAnimationDurationForMoving = base::Milliseconds(300);
+
 // Checks if the `selected_date` is local time today.
 bool IsToday(const base::Time::Exploded& selected_date);
 
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index 631e75c7..9f1e49a 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -57,13 +57,6 @@
 // we wait before fetchiung more events.
 constexpr base::TimeDelta kScrollingSettledTimeout = base::Milliseconds(100);
 
-// Duration of opacity animation for visibility changes.
-constexpr base::TimeDelta kAnimationDurationForVisibility =
-    base::Milliseconds(100);
-
-// Duration of moving animation.
-constexpr base::TimeDelta kAnimationDurationForMoving = base::Milliseconds(300);
-
 // Duration of the delay for modifying opacity.
 constexpr base::TimeDelta kDelayVisibilityAnimationDuration =
     base::Milliseconds(200);
@@ -247,16 +240,26 @@
           kScrollingSettledTimeout,
           base::BindRepeating(&CalendarView::OnScrollingSettledTimerFired,
                               base::Unretained(this))),
-      animation_restart_timer_(FROM_HERE,
-                               kAnimationDisablingTimeout,
-                               base::BindRepeating(
-                                   [](CalendarView* calendar_view) {
-                                     if (!calendar_view)
-                                       return;
-                                     calendar_view->should_show_animation_ =
-                                         true;
-                                   },
-                                   base::Unretained(this))) {
+      header_animation_restart_timer_(
+          FROM_HERE,
+          kAnimationDisablingTimeout,
+          base::BindRepeating(
+              [](CalendarView* calendar_view) {
+                if (!calendar_view)
+                  return;
+                calendar_view->set_should_header_animate(true);
+              },
+              base::Unretained(this))),
+      months_animation_restart_timer_(
+          FROM_HERE,
+          kAnimationDisablingTimeout,
+          base::BindRepeating(
+              [](CalendarView* calendar_view) {
+                if (!calendar_view)
+                  return;
+                calendar_view->set_should_months_animate(true);
+              },
+              base::Unretained(this))) {
   CreateTitleRow(IDS_ASH_CALENDAR_TITLE);
 
   // Add the header.
@@ -326,12 +329,10 @@
 }
 
 CalendarView::~CalendarView() {
-  // Removes the month views to remove its dependency from
-  // `CalendarViewController`, since these views are destructed after the
+  // Removes child views including month views to remove its dependency from
+  // `CalendarViewController`, since month views are destructed after the
   // controller.
-  content_view_->RemoveChildViewT(previous_month_);
-  content_view_->RemoveChildViewT(current_month_);
-  content_view_->RemoveChildViewT(next_month_);
+  content_view_->RemoveAllChildViews();
 }
 
 void CalendarView::Init() {
@@ -449,8 +450,8 @@
   header_->layer()->SetOpacity(1.0f);
   header_->layer()->SetTransform(gfx::Transform());
   scrolling_settled_timer_.Reset();
-  if (!should_show_animation_)
-    animation_restart_timer_.Reset();
+  if (!should_header_animate_)
+    header_animation_restart_timer_.Reset();
 }
 
 void CalendarView::RestoreMonthStatus(bool is_scrolling_up) {
@@ -473,8 +474,8 @@
     next_month_->layer()->SetOpacity(1.0f);
     next_month_->layer()->SetTransform(gfx::Transform());
   }
-  if (!should_show_animation_)
-    animation_restart_timer_.Reset();
+  if (!should_months_animate_)
+    months_animation_restart_timer_.Reset();
 }
 
 void CalendarView::ScrollToToday() {
@@ -588,95 +589,54 @@
 }
 
 void CalendarView::OnMonthChanged(const base::Time::Exploded current_month) {
-  if (!should_show_animation_) {
+  if (!should_header_animate_) {
     UpdateHeaders();
     RestoreHeadersStatus();
     return;
   }
+
+  header_->layer()->SetTransform(gfx::Transform());
+  header_->layer()->SetOpacity(0.0f);
+  UpdateHeaders();
+
   const int header_height = header_->GetPreferredSize().height();
-  const gfx::Vector2dF moving_location = gfx::Vector2dF(
+  gfx::Vector2dF moving_location = gfx::Vector2dF(
       0, calendar_view_controller_->was_on_later_month() ? -header_height / 2
                                                          : header_height / 2);
-  gfx::Transform header_moving = gfx::TransformAboutPivot(
+  gfx::Transform initial_state = gfx::TransformAboutPivot(
       header_->GetLocalBounds().CenterPoint(), gfx::Transform());
-  header_moving.Translate(moving_location);
-
-  should_show_animation_ = false;
-
-  auto on_animation_aborted = [](base::WeakPtr<CalendarView> calendar_view) {
-    if (!calendar_view)
-      return;
-    calendar_view->UpdateHeaders();
-    calendar_view->RestoreHeadersStatus();
-  };
-
-  auto on_animation_ended = [](base::WeakPtr<CalendarView> calendar_view) {
-    if (!calendar_view)
-      return;
-    DCHECK(!calendar_view->header_->layer()->GetAnimator()->is_animating());
-
-    calendar_view->header_->layer()->SetTransform(gfx::Transform());
-    calendar_view->header_->layer()->SetOpacity(0.0f);
-    calendar_view->UpdateHeaders();
-    const int header_height =
-        calendar_view->header_->GetPreferredSize().height();
-    gfx::Vector2dF moving_location = gfx::Vector2dF(
-        0, calendar_view->calendar_view_controller_->was_on_later_month()
-               ? header_height / 2
-               : -header_height / 2);
-    gfx::Transform initial_state = gfx::TransformAboutPivot(
-        calendar_view->header_->GetLocalBounds().CenterPoint(),
-        gfx::Transform());
-    initial_state.Translate(moving_location);
-    views::AnimationBuilder()
-        .SetPreemptionStrategy(
-            ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
-        .OnEnded(base::BindOnce(
-            [](base::WeakPtr<CalendarView> calendar_view) {
-              if (!calendar_view)
-                return;
-              calendar_view->should_show_animation_ = true;
-              calendar_view->scrolling_settled_timer_.Reset();
-            },
-            calendar_view->weak_factory_.GetWeakPtr()))
-        .OnAborted(base::BindOnce(
-            [](base::WeakPtr<CalendarView> calendar_view) {
-              if (!calendar_view)
-                return;
-              calendar_view->UpdateHeaders();
-              calendar_view->RestoreHeadersStatus();
-            },
-            calendar_view->weak_factory_.GetWeakPtr()))
-        .Once()
-        .SetDuration(base::TimeDelta())
-        .SetTransform(calendar_view->header_, std::move(initial_state))
-        .Then()
-        .SetDuration(kAnimationDurationForMoving)
-        .SetTransform(calendar_view->header_, gfx::Transform(),
-                      gfx::Tween::EASE_OUT_2)
-        .At(base::Milliseconds(0))
-        .SetDuration(kDelayVisibilityAnimationDuration)
-        .Then()
-        .SetDuration(kAnimationDurationForVisibility)
-        .SetOpacity(calendar_view->header_, 1.0);
-    calendar_view->should_show_animation_ = true;
-    calendar_view->scrolling_settled_timer_.Reset();
-  };
+  initial_state.Translate(moving_location);
+  set_should_header_animate(false);
 
   views::AnimationBuilder()
       .SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
-      .OnEnded(base::BindOnce(on_animation_ended, weak_factory_.GetWeakPtr()))
-      .OnAborted(
-          base::BindOnce(on_animation_aborted, weak_factory_.GetWeakPtr()))
+      .OnEnded(base::BindOnce(
+          [](base::WeakPtr<CalendarView> calendar_view) {
+            if (!calendar_view)
+              return;
+            calendar_view->set_should_header_animate(true);
+            calendar_view->reset_scrolling_settled_timer();
+          },
+          weak_factory_.GetWeakPtr()))
+      .OnAborted(base::BindOnce(
+          [](base::WeakPtr<CalendarView> calendar_view) {
+            if (!calendar_view)
+              return;
+            calendar_view->UpdateHeaders();
+            calendar_view->RestoreHeadersStatus();
+          },
+          weak_factory_.GetWeakPtr()))
       .Once()
-      .SetDuration(kAnimationDurationForMoving)
-      .SetTransform(header_, std::move(header_moving), gfx::Tween::EASE_OUT_2)
+      .SetTransform(header_, std::move(initial_state))
+      .Then()
+      .SetDuration(calendar_utils::kAnimationDurationForMoving)
+      .SetTransform(header_, gfx::Transform(), gfx::Tween::EASE_OUT_2)
       .At(base::Milliseconds(0))
       .SetDuration(kDelayVisibilityAnimationDuration)
       .Then()
-      .SetDuration(kAnimationDurationForVisibility)
-      .SetOpacity(header_, 0.0);
+      .SetDuration(calendar_utils::kAnimationDurationForVisibility)
+      .SetOpacity(header_, 1.0);
 }
 
 void CalendarView::OnEventsFetched(
@@ -804,17 +764,17 @@
 }
 
 void CalendarView::ScrollOneMonthWithAnimation(bool is_scrolling_up) {
-  // TODO(https://crbug.com/1238927). Scroll the height of one row each time if
-  // the event list is shown.
-  if (event_list_)
-    return;
-
   if (is_resetting_scroll_)
     return;
 
+  if (event_list_) {
+    ScrollOneRowWithAnimation(is_scrolling_up);
+    return;
+  }
+
   // If there's already an existing animation, restores each layer's visibility
   // and position.
-  if (!should_show_animation_) {
+  if (!should_months_animate_) {
     if (is_scrolling_up) {
       ScrollUpOneMonthAndAutoScroll();
       return;
@@ -823,13 +783,15 @@
     return;
   }
 
-  should_show_animation_ = false;
-  gfx::Vector2dF moving_up_location =
-      gfx::Vector2dF(0, previous_month_->GetPreferredSize().height() +
-                            current_label_->GetPreferredSize().height());
-  gfx::Vector2dF moving_down_location =
-      gfx::Vector2dF(0, -current_month_->GetPreferredSize().height() -
-                            next_label_->GetPreferredSize().height());
+  set_should_months_animate(false);
+  gfx::Vector2dF moving_up_location = gfx::Vector2dF(
+      0, previous_month_->GetPreferredSize().height() +
+             current_label_->GetPreferredSize().height() +
+             (scroll_view_->GetVisibleRect().y() - current_month_->y()));
+  gfx::Vector2dF moving_down_location = gfx::Vector2dF(
+      0, -current_month_->GetPreferredSize().height() -
+             next_label_->GetPreferredSize().height() +
+             (scroll_view_->GetVisibleRect().y() - current_month_->y()));
 
   gfx::Transform current_month_moving = gfx::TransformAboutPivot(
       current_month_->GetLocalBounds().CenterPoint(), gfx::Transform());
@@ -849,6 +811,14 @@
       next_month_->GetLocalBounds().CenterPoint(), gfx::Transform());
   next_month_moving.Translate(moving_down_location);
 
+  const int header_height = header_->GetPreferredSize().height();
+  const gfx::Vector2dF moving_location = gfx::Vector2dF(
+      0, calendar_view_controller_->was_on_later_month() ? header_height / 2
+                                                         : -header_height / 2);
+  gfx::Transform header_moving = gfx::TransformAboutPivot(
+      header_->GetLocalBounds().CenterPoint(), gfx::Transform());
+  header_moving.Translate(moving_location);
+
   views::AnimationBuilder()
       .SetPreemptionStrategy(
           ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
@@ -856,7 +826,7 @@
           [](base::WeakPtr<CalendarView> calendar_view, bool is_scrolling_up) {
             if (!calendar_view)
               return;
-            calendar_view->should_show_animation_ = true;
+            calendar_view->set_should_months_animate(true);
             is_scrolling_up ? calendar_view->ScrollUpOneMonthAndAutoScroll()
                             : calendar_view->ScrollDownOneMonthAndAutoScroll();
           },
@@ -870,7 +840,7 @@
           },
           weak_factory_.GetWeakPtr(), is_scrolling_up))
       .Once()
-      .SetDuration(kAnimationDurationForMoving * 2)
+      .SetDuration(calendar_utils::kAnimationDurationForMoving * 2)
       .SetTransform(current_month_, std::move(current_month_moving),
                     gfx::Tween::EASE_OUT_2)
       .SetTransform(
@@ -881,7 +851,60 @@
                     std::move(is_scrolling_up ? previous_month_moving
                                               : next_month_moving),
                     gfx::Tween::EASE_OUT_2)
-      .SetOpacity(current_month_, 0.0);
+      .At(calendar_utils::kAnimationDurationForMoving)
+      .SetDuration(calendar_utils::kAnimationDurationForMoving)
+      .SetTransform(header_, std::move(header_moving), gfx::Tween::EASE_OUT_2)
+      .At(calendar_utils::kAnimationDurationForMoving)
+      .SetDuration(kDelayVisibilityAnimationDuration)
+      .Then()
+      .SetDuration(calendar_utils::kAnimationDurationForVisibility)
+      .SetOpacity(header_, 0.0);
+}
+
+void CalendarView::ScrollOneRowWithAnimation(bool is_scrolling_up) {
+  scroll_view_->SetVerticalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kHiddenButEnabled);
+  base::AutoReset<bool> is_resetting_scrolling(&is_resetting_scroll_, true);
+
+  // Scrolls to the last row of the previous month if it's currently on the
+  // first row and scrolling up.
+  if (is_scrolling_up &&
+      calendar_view_controller_->GetExpandedRowIndex() == 0) {
+    ScrollUpOneMonth();
+    calendar_view_controller_->set_expanded_row_index(
+        current_month_->last_row_index());
+    const int row_height = calendar_view_controller_->GetExpandedRowIndex() *
+                           calendar_view_controller_->row_height();
+    scroll_view_->ScrollToPosition(scroll_view_->vertical_scroll_bar(),
+                                   PositionOfCurrentMonth() + row_height);
+    scroll_view_->SetVerticalScrollBarMode(
+        views::ScrollView::ScrollBarMode::kDisabled);
+    return;
+  }
+
+  // Scrolls to the first row of the next month if it's currently on the
+  // last row and scrolling down.
+  if (!is_scrolling_up && calendar_view_controller_->GetExpandedRowIndex() ==
+                              current_month_->last_row_index()) {
+    ScrollDownOneMonth();
+    calendar_view_controller_->set_expanded_row_index(0);
+    scroll_view_->ScrollToPosition(scroll_view_->vertical_scroll_bar(),
+                                   PositionOfCurrentMonth());
+    scroll_view_->SetVerticalScrollBarMode(
+        views::ScrollView::ScrollBarMode::kDisabled);
+    return;
+  }
+
+  calendar_view_controller_->set_expanded_row_index(
+      calendar_view_controller_->GetExpandedRowIndex() +
+      (is_scrolling_up ? -1 : 1));
+  const int row_height = calendar_view_controller_->GetExpandedRowIndex() *
+                         calendar_view_controller_->row_height();
+  scroll_view_->ScrollToPosition(scroll_view_->vertical_scroll_bar(),
+                                 PositionOfCurrentMonth() + row_height);
+  scroll_view_->SetVerticalScrollBarMode(
+      views::ScrollView::ScrollBarMode::kDisabled);
+  return;
 }
 
 void CalendarView::OnEvent(ui::Event* event) {
@@ -996,6 +1019,8 @@
   if (is_resetting_scroll_)
     return;
 
+  base::AutoReset<bool> disable_header_animation(&should_header_animate_,
+                                                 false);
   // Scrolls to the previous month if the current label is moving down and
   // passing the top of the visible area.
   if (scroll_view_->GetVisibleRect().y() <= current_label_->y()) {
diff --git a/ash/system/time/calendar_view.h b/ash/system/time/calendar_view.h
index bc50dcd..3de16a9 100644
--- a/ash/system/time/calendar_view.h
+++ b/ash/system/time/calendar_view.h
@@ -143,6 +143,9 @@
   // the current month's first row.
   void ScrollOneMonthWithAnimation(bool is_scrolling_up);
 
+  // Scrolls up/down one row based on `is_scrolling_up`.
+  void ScrollOneRowWithAnimation(bool is_scrolling_up);
+
   // Back to the landing view.
   void ResetToToday();
 
@@ -150,7 +153,7 @@
   void UpdateHeaders();
 
   // Resets the `header_`'s opacity and position. Also resets
-  // `scrolling_settled_timer_` and `animation_restart_timer_`.
+  // `scrolling_settled_timer_` and `header_animation_restart_timer_`.
   void RestoreHeadersStatus();
 
   // Resets the the month views' opacity and position. In case the animation is
@@ -179,6 +182,17 @@
 
   std::unique_ptr<CalendarViewController> calendar_view_controller_;
 
+  // Setters for animation flags.
+  void set_should_header_animate(bool should_animate) {
+    should_header_animate_ = should_animate;
+  }
+  void set_should_months_animate(bool should_animate) {
+    should_months_animate_ = should_animate;
+  }
+
+  // Reset `scrolling_settled_timer_`.
+  void reset_scrolling_settled_timer() { scrolling_settled_timer_.Reset(); }
+
   // The content of the `scroll_view_`, which carries months and month labels.
   // Owned by `CalendarView`.
   views::View* content_view_ = nullptr;
@@ -202,18 +216,24 @@
   // don't need to check if we need to update the month or not.
   bool is_resetting_scroll_ = false;
 
-  // It's true if it should animate, but false when it is currently animating or
+  // It's true if the header should animate, but false when it is currently
+  // animating, or header changing from mouse scroll (not from the buttons) or
   // cooling down from the last animation.
-  bool should_show_animation_ = true;
+  bool should_header_animate_ = true;
+
+  // It's true if the month views should animate, but false when it is currently
+  // animating, or cooling down from the last animation.
+  bool should_months_animate_ = true;
 
   // Timer that fires when we've "settled" on, i.e. finished scrolling to, a
   // currently-visible month
   base::RetainingOneShotTimer scrolling_settled_timer_;
 
-  // Timer that enables the updating month animation. When the month keeps
-  // getting changed, the animation will be disabled and the cool-down duration
-  // is `kAnimationDisablingTimeout` ms to enable the next animation.
-  base::RetainingOneShotTimer animation_restart_timer_;
+  // Timers that enable the updating month/header animations. When the month
+  // keeps getting changed, the animation will be disabled and the cool-down
+  // duration is `kAnimationDisablingTimeout` ms to enable the next animation.
+  base::RetainingOneShotTimer header_animation_restart_timer_;
+  base::RetainingOneShotTimer months_animation_restart_timer_;
 
   base::CallbackListSubscription on_contents_scrolled_subscription_;
   base::ScopedObservation<CalendarViewController,
diff --git a/ash/system/time/calendar_view_controller.cc b/ash/system/time/calendar_view_controller.cc
index da7d7163..3691a77 100644
--- a/ash/system/time/calendar_view_controller.cc
+++ b/ash/system/time/calendar_view_controller.cc
@@ -160,6 +160,11 @@
   return calendar_utils::GetMonthName(current_date_);
 }
 
+int CalendarViewController::GetExpandedRowIndex() const {
+  DCHECK(is_event_list_showing_);
+  return expanded_row_index_;
+}
+
 int CalendarViewController::GetTodayRowTopHeight() const {
   return (today_row_ - 1) * row_height_;
 }
@@ -292,6 +297,7 @@
   }
   selected_date_ = selected_date;
   selected_date_row_index_ = row_index;
+  expanded_row_index_ = row_index;
 
   // Notify observers.
   for (auto& observer : observers_)
diff --git a/ash/system/time/calendar_view_controller.h b/ash/system/time/calendar_view_controller.h
index e70c72d..c7c550f6 100644
--- a/ash/system/time/calendar_view_controller.h
+++ b/ash/system/time/calendar_view_controller.h
@@ -121,16 +121,21 @@
   // scrolling to this row when the event list is expanded.
   int selected_date_row_index() { return selected_date_row_index_; }
 
-  // Getters of the today's row position, top and bottom.
-  int GetTodayRowTopHeight() const;
-  int GetTodayRowBottomHeight() const;
-
-  // Getters and setters of the today's row number and row height.
+  // Getters and setters: the row index when the event list view is showing,
+  // today's row number and today's row height.
+  int GetExpandedRowIndex() const;
+  void set_expanded_row_index(int row_index) {
+    expanded_row_index_ = row_index;
+  }
   int today_row() const { return today_row_; }
   void set_today_row(int row) { today_row_ = row; }
   int row_height() const { return row_height_; }
   void set_row_height(int height) { row_height_ = height; }
 
+  // Getters of the today's row position, top and bottom.
+  int GetTodayRowTopHeight() const;
+  int GetTodayRowBottomHeight() const;
+
   // Requests more events as needed.
   void FetchEvents();
 
@@ -256,6 +261,9 @@
   // The row index of the currently selected date.
   int selected_date_row_index_;
 
+  // The current row index when the event list view is shown.
+  int expanded_row_index_;
+
   // The event list of the currently selected date.
   SingleDayEventList* selected_date_events_;
 
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index 2fb54d4..894b9dc 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -7,6 +7,8 @@
 #include "ash/shell.h"
 #include "ash/style/icon_button.h"
 #include "ash/system/time/calendar_month_view.h"
+#include "ash/system/time/calendar_unittest_utils.h"
+#include "ash/system/time/calendar_utils.h"
 #include "ash/system/time/calendar_view_controller.h"
 #include "ash/system/tray/detailed_view_delegate.h"
 #include "ash/test/ash_test_base.h"
@@ -584,10 +586,10 @@
   }
 
   void TearDown() override {
-    widget_.reset();
     delegate_.reset();
     tray_controller_.reset();
     tray_model_.reset();
+    widget_.reset();
 
     AshTestBase::TearDown();
   }
@@ -597,11 +599,32 @@
         delegate_.get(), tray_controller_.get()));
   }
 
+  void UpdateMonth(base::Time date) {
+    calendar_view_->calendar_view_controller()->UpdateMonth(date);
+    calendar_view_->content_view_->RemoveAllChildViews();
+    calendar_view_->SetMonthViews();
+  }
+
   CalendarView* calendar_view() { return calendar_view_; }
 
   views::Label* month_header() { return calendar_view_->header_->header_; }
   views::Label* header_year() { return calendar_view_->header_->header_year_; }
   CalendarHeaderView* header() { return calendar_view_->header_; }
+  CalendarMonthView* current_month() { return calendar_view_->current_month_; }
+  CalendarMonthView* previous_month() {
+    return calendar_view_->previous_month_;
+  }
+  CalendarMonthView* next_month() { return calendar_view_->next_month_; }
+  views::View* previous_label() { return calendar_view_->previous_label_; }
+  views::View* current_label() { return calendar_view_->current_label_; }
+  views::View* next_label() { return calendar_view_->next_label_; }
+
+  void ScrollUpOneMonth() {
+    calendar_view_->ScrollOneMonthWithAnimation(/*is_scrolling_up=*/true);
+  }
+  void ScrollDownOneMonth() {
+    calendar_view_->ScrollOneMonthWithAnimation(/*is_scrolling_up=*/false);
+  }
 
  private:
   std::unique_ptr<views::Widget> widget_;
@@ -613,6 +636,7 @@
   static base::Time fake_time_;
 };
 
+// The header should show the new header with animation when there's an update.
 TEST_F(CalendarViewAnimationTest, HeaderAnimation) {
   ui::ScopedAnimationDurationScaleMode test_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
@@ -623,9 +647,11 @@
   CreateCalendarView();
   // Gives it a duration to let the animation finish and pass the cool down
   // duration. The same for the other 3s duration.
-  task_environment()->FastForwardBy(base::Seconds(3));
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
   calendar_view()->calendar_view_controller()->UpdateMonth(date);
-  task_environment()->FastForwardBy(base::Seconds(3));
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
 
   EXPECT_EQ(u"October", month_header()->GetText());
   EXPECT_EQ(u"2021", header_year()->GetText());
@@ -634,18 +660,11 @@
   calendar_view()->calendar_view_controller()->UpdateMonth(date +
                                                            base::Days(33));
 
-  // The Opacity is updated from 1.0f to 0.0f after 200ms delay duration.
-  EXPECT_EQ(1.0f, header()->layer()->opacity());
-  task_environment()->FastForwardBy(base::Milliseconds(100));
-  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
-  EXPECT_EQ(1.0f, header()->layer()->opacity());
-  task_environment()->FastForwardBy(base::Milliseconds(200));
-
   // To prevent flakiness, fast forward until the header changes (see
   // crbug/1270161). The second animation starts after the header is updated to
   // the new month.
   while (u"November" != month_header()->GetText()) {
-    task_environment()->FastForwardBy(base::Milliseconds(80));
+    task_environment()->FastForwardBy(base::Milliseconds(30));
   }
   // The opacity is updated to 0 after the first animation ends.
   EXPECT_EQ(0.0f, header()->layer()->opacity());
@@ -656,11 +675,13 @@
   EXPECT_EQ(u"2021", header_year()->GetText());
 
   // The Opacity is back from 0.0f to 1.0 after 200ms delay duration.
-  task_environment()->FastForwardBy(base::Milliseconds(100));
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForVisibility);
   EXPECT_EQ(0.0f, header()->layer()->opacity());
 
   // Gives it a duration to let the animation finish.
-  task_environment()->FastForwardBy(base::Milliseconds(1000));
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
   EXPECT_EQ(1.0f, header()->layer()->opacity());
 
   // The header is still with the updated new month after all animation
@@ -669,4 +690,109 @@
   EXPECT_EQ(u"2021", header_year()->GetText());
 }
 
+// The month views and header should animate when scrolling up or down.
+TEST_F(CalendarViewAnimationTest, MonthAndHeaderAnimation) {
+  ui::ScopedAnimationDurationScaleMode test_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+  base::Time date;
+  ASSERT_TRUE(base::Time::FromString("24 Oct 2021 10:00 GMT", &date));
+
+  CreateCalendarView();
+  // Gives it a duration to let the animation finish and pass the cool down
+  // duration.
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
+  UpdateMonth(date);
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
+
+  EXPECT_EQ(u"October", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+
+  // Scrolls to the next month.
+  ScrollDownOneMonth();
+
+  // If scrolls down, the month views that will animate are `current_month_`,
+  // `next_month_` and 'next_label_`.
+  EXPECT_EQ(1.0f, header()->layer()->opacity());
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForVisibility);
+  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(next_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(next_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(previous_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(previous_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(current_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_EQ(u"October", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+
+  // The header animation starts from 300ms. Its Opacity is updated from 1.0f to
+  // 0.0f after 300+200ms delay duration.
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForMoving);
+  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
+  EXPECT_EQ(1.0f, header()->layer()->opacity());
+
+  // To prevent flakiness, fast forward until the header changes (see
+  // crbug/1270161). The second animation starts after the header is updated to
+  // the new month.
+  while (u"November" != month_header()->GetText()) {
+    task_environment()->FastForwardBy(base::Milliseconds(30));
+  }
+
+  // The opacity is updated to 0 after the first animation ends.
+  EXPECT_EQ(0.0f, header()->layer()->opacity());
+
+  // Now the header is updated to the new month and year before it starts
+  // showing up.
+  EXPECT_EQ(u"November", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+
+  // The Opacity is back from 0.0f to 1.0 after 200ms delay duration.
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForVisibility);
+  EXPECT_EQ(0.0f, header()->layer()->opacity());
+
+  // Gives it a duration to let the animation finish.
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
+  EXPECT_EQ(1.0f, header()->layer()->opacity());
+
+  // The header is still with the updated new month after all animation
+  // finished.
+  EXPECT_EQ(u"November", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+
+  // Scrolls to the previous month.
+  ScrollUpOneMonth();
+
+  // If scrolls up, the month views that will animate are `current_month_`,
+  // `previous_month_` and 'current_label_`.
+  EXPECT_EQ(1.0f, header()->layer()->opacity());
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForVisibility);
+  EXPECT_TRUE(current_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(current_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(previous_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(next_month()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(previous_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_FALSE(next_label()->layer()->GetAnimator()->is_animating());
+  EXPECT_EQ(u"November", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+
+  // The header animation starts from 300ms. Its Opacity is updated from 1.0f to
+  // 0.0f after 300+200ms delay duration.
+  task_environment()->FastForwardBy(
+      calendar_utils::kAnimationDurationForMoving);
+  EXPECT_TRUE(header()->layer()->GetAnimator()->is_animating());
+  EXPECT_EQ(1.0f, header()->layer()->opacity());
+
+  task_environment()->FastForwardBy(
+      calendar_test_utils::kAnimationSettleDownDuration);
+
+  EXPECT_EQ(u"October", month_header()->GetText());
+  EXPECT_EQ(u"2021", header_year()->GetText());
+}
+
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index e44b704..53ee3e3 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -2565,6 +2565,11 @@
     WallpaperInfo info) {
   if (!CanSetUserWallpaper(account_id))
     return;
+  // We don't sync for background users because we don't want to update the
+  // wallpaper for background users. Instead, we call
+  // HandleWallpaperInfoSyncedIn again in OnActiveUserPrefServiceChanged.
+  if (!IsActiveUser(account_id))
+    return;
   switch (info.type) {
     case WallpaperType::kCustomized:
       HandleCustomWallpaperInfoSyncedIn(account_id, info);
@@ -2883,7 +2888,6 @@
                             weak_factory_.GetWeakPtr()));
     return;
   }
-
   base::FilePath path_in_prefs = base::FilePath(info.location);
   std::string file_name = path_in_prefs.BaseName().value();
   ReadAndDecodeWallpaper(
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index 0f153c5..5f1abc1b 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -305,7 +305,7 @@
   bool IsActiveUserWallpaperControlledByPolicy() override;
   WallpaperInfo GetActiveUserWallpaperInfo() override;
   bool ShouldShowWallpaperSetting() override;
-  void SetDailyRefreshCollectionId(const AccountId& accound_id,
+  void SetDailyRefreshCollectionId(const AccountId& account_id,
                                    const std::string& collection_id) override;
   std::string GetDailyRefreshCollectionId(
       const AccountId& account_id) const override;
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index b22179c..8ffd5d85 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -3546,6 +3546,25 @@
   EXPECT_EQ(controller_->GetWallpaperType(), WallpaperType::kOnline);
 }
 
+TEST_F(WallpaperControllerWallpaperWebUiTest,
+       HandleWallpaperInfoSyncedInactiveUser) {
+  CacheOnlineWallpaper(kDummyUrl);
+
+  // Make account_id_1 the inactive user.
+  SimulateUserLogin(account_id_2);
+
+  // Attempt to set an online wallpaper without providing the image data. Verify
+  // it succeeds this time because |SetOnlineWallpaperFromData| has saved the
+  // file.
+  ClearWallpaperCount();
+  PutWallpaperInfoInPrefs(account_id_1, InfoWithType(WallpaperType::kOnline),
+                          GetProfilePrefService(account_id_1),
+                          prefs::kSyncableWallpaperInfo);
+  RunAllTasksUntilIdle();
+  EXPECT_EQ(0, GetWallpaperCount());
+  EXPECT_NE(controller_->GetWallpaperType(), WallpaperType::kOnline);
+}
+
 TEST_F(WallpaperControllerWallpaperWebUiTest, UpdateDailyRefreshWallpaper) {
   std::string expected{"fun_collection"};
   SimulateUserLogin(account_id_1);
diff --git a/ash/webui/sample_system_web_app_ui/resources/timer.js b/ash/webui/sample_system_web_app_ui/resources/timer.js
index 011abc9..902d421 100644
--- a/ash/webui/sample_system_web_app_ui/resources/timer.js
+++ b/ash/webui/sample_system_web_app_ui/resources/timer.js
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-const myWorker = new SharedWorker('worker.js');
+const workerUrlPolicy = trustedTypes.createPolicy(
+    'worker-js-static',
+    {createScriptURL: () => 'chrome://sample-system-web-app/worker.js'});
+const myWorker = new SharedWorker(workerUrlPolicy.createScriptURL(''));
 
 myWorker.port.onmessage = (e) => {
   window.close();
diff --git a/ash/wm/desks/desk_name_view.cc b/ash/wm/desks/desk_name_view.cc
index 4398fa7..9c200a72 100644
--- a/ash/wm/desks/desk_name_view.cc
+++ b/ash/wm/desks/desk_name_view.cc
@@ -7,20 +7,13 @@
 #include <memory>
 
 #include "ash/shell.h"
-#include "ash/style/ash_color_provider.h"
 #include "ash/wm/desks/desk_mini_view.h"
 #include "ash/wm/desks/desks_bar_view.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
-#include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
-#include "ui/views/accessibility/accessibility_paint_checks.h"
-#include "ui/views/background.h"
 #include "ui/views/focus/focus_manager.h"
-#include "ui/views/native_cursor.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -64,50 +57,11 @@
   focus_manager->SetStoredFocusView(nullptr);
 }
 
-bool DeskNameView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
-  // The default behavior of the tab key is that it moves the focus to the next
-  // available DeskNameView.
-  // We want that to be handled by OverviewHighlightController as part of moving
-  // the highlight forward or backward when tab or shift+tab are pressed.
-  if (event.key_code() == ui::VKEY_TAB)
-    return true;
-
-  return false;
-}
-
-void DeskNameView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  Textfield::GetAccessibleNodeData(node_data);
-  // When Bento is enabled and the user creates a new desk, |full_text_| will be
-  // empty but the accesssible name for |this| will be the default desk name.
-  node_data->SetName(full_text_.empty() ? GetAccessibleName() : full_text_);
-}
-
-views::View* DeskNameView::GetView() {
-  return this;
-}
-
-void DeskNameView::MaybeActivateHighlightedView() {
-  RequestFocus();
-}
-
-void DeskNameView::MaybeCloseHighlightedView() {}
-
-void DeskNameView::MaybeSwapHighlightedView(bool right) {}
-
 void DeskNameView::OnViewHighlighted() {
-  UpdateBorderState();
+  LabelTextfield::OnViewHighlighted();
   mini_view_->owner_bar()->ScrollToShowMiniViewIfNecessary(mini_view_);
 }
 
-void DeskNameView::OnViewUnhighlighted() {
-  UpdateBorderState();
-}
-
-void DeskNameView::UpdateBorderState() {
-  border_ptr_->SetFocused(IsViewHighlighted() || HasFocus());
-  SchedulePaint();
-}
-
 BEGIN_METADATA(DeskNameView, LabelTextfield)
 END_METADATA
 
diff --git a/ash/wm/desks/desk_name_view.h b/ash/wm/desks/desk_name_view.h
index db1fb312..50bd127 100644
--- a/ash/wm/desks/desk_name_view.h
+++ b/ash/wm/desks/desk_name_view.h
@@ -7,8 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/wm/desks/label_textfield.h"
-#include "ash/wm/overview/overview_highlightable_view.h"
-#include "ash/wm/wm_highlight_item_border.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 
 namespace ash {
@@ -18,10 +16,9 @@
 // Defines a textfield styled to normally look like a label. Allows modifying
 // the name of its corresponding desk. It can be highlighted and activated by
 // the OverviewHighlightController. Inherits an API to elide long desk names.
-// TODO(richui): In a follow up CL, refactor the renaming logic, and see if
-// there are more functions we can extract into `LabelTextfield`.
-class ASH_EXPORT DeskNameView : public LabelTextfield,
-                                public OverviewHighlightableView {
+// When Bento is enabled and the user creates a new desk, the accessible name
+// for `this` will be the default desk name.
+class ASH_EXPORT DeskNameView : public LabelTextfield {
  public:
   METADATA_HEADER(DeskNameView);
 
@@ -38,20 +35,9 @@
   static void CommitChanges(views::Widget* widget);
 
   // LabelTextfield:
-  bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-
-  // OverviewHighlightableView:
-  views::View* GetView() override;
-  void MaybeActivateHighlightedView() override;
-  void MaybeCloseHighlightedView() override;
-  void MaybeSwapHighlightedView(bool right) override;
   void OnViewHighlighted() override;
-  void OnViewUnhighlighted() override;
 
  private:
-  void UpdateBorderState();
-
   // The mini view that associated with this name view.
   DeskMiniView* const mini_view_;
 };
diff --git a/ash/wm/desks/label_textfield.cc b/ash/wm/desks/label_textfield.cc
index 92bf4853..9c7a2054 100644
--- a/ash/wm/desks/label_textfield.cc
+++ b/ash/wm/desks/label_textfield.cc
@@ -73,6 +73,19 @@
   return size;
 }
 
+bool LabelTextfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
+  // The default behavior of the tab key is that it moves the focus to the next
+  // available view.
+  // We want that to be handled by OverviewHighlightController as part of moving
+  // the highlight forward or backward when tab or shift+tab are pressed.
+  return event.key_code() == ui::VKEY_TAB;
+}
+
+void LabelTextfield::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  Textfield::GetAccessibleNodeData(node_data);
+  node_data->SetName(full_text_.empty() ? GetAccessibleName() : full_text_);
+}
+
 void LabelTextfield::OnMouseEntered(const ui::MouseEvent& event) {
   UpdateViewAppearance();
 }
@@ -101,8 +114,28 @@
   return views::GetNativeIBeamCursor();
 }
 
+views::View* LabelTextfield::GetView() {
+  return this;
+}
+
+void LabelTextfield::MaybeActivateHighlightedView() {
+  RequestFocus();
+}
+
+void LabelTextfield::MaybeCloseHighlightedView() {}
+
+void LabelTextfield::MaybeSwapHighlightedView(bool right) {}
+
+void LabelTextfield::OnViewHighlighted() {
+  UpdateBorderState();
+}
+
+void LabelTextfield::OnViewUnhighlighted() {
+  UpdateBorderState();
+}
+
 void LabelTextfield::UpdateBorderState() {
-  border_ptr_->SetFocused(HasFocus());
+  border_ptr_->SetFocused(IsViewHighlighted() || HasFocus());
   SchedulePaint();
 }
 
diff --git a/ash/wm/desks/label_textfield.h b/ash/wm/desks/label_textfield.h
index 53870f74b..d2114b27 100644
--- a/ash/wm/desks/label_textfield.h
+++ b/ash/wm/desks/label_textfield.h
@@ -5,6 +5,8 @@
 #ifndef ASH_WM_DESKS_LABEL_TEXTFIELD_H_
 #define ASH_WM_DESKS_LABEL_TEXTFIELD_H_
 
+#include "ash/ash_export.h"
+#include "ash/wm/overview/overview_highlightable_view.h"
 #include "ash/wm/wm_highlight_item_border.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -14,7 +16,8 @@
 // Defines a textfield styled so when it's not focused, it looks like a normal
 // label. It provides an API to elide long names.
 // TODO(minch): Unify this to ash/style.
-class LabelTextfield : public views::Textfield {
+class ASH_EXPORT LabelTextfield : public views::Textfield,
+                                  public OverviewHighlightableView {
  public:
   METADATA_HEADER(LabelTextfield);
 
@@ -35,11 +38,21 @@
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
+  bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
   void OnThemeChanged() override;
   gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
 
+  // OverviewHighlightableView:
+  views::View* GetView() override;
+  void MaybeActivateHighlightedView() override;
+  void MaybeCloseHighlightedView() override;
+  void MaybeSwapHighlightedView(bool right) override;
+  void OnViewHighlighted() override;
+  void OnViewUnhighlighted() override;
+
  protected:
   // Owned by this View via `View::border_`. This is just a convenient pointer
   // to it.
diff --git a/ash/wm/desks/templates/desks_templates_item_view.h b/ash/wm/desks/templates/desks_templates_item_view.h
index 545fedd..6dff561d 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.h
+++ b/ash/wm/desks/templates/desks_templates_item_view.h
@@ -37,6 +37,8 @@
   DesksTemplatesItemView& operator=(const DesksTemplatesItemView&) = delete;
   ~DesksTemplatesItemView() override;
 
+  DesksTemplatesNameView* name_view() const { return name_view_; }
+
   // Updates the visibility state of the delete and launch buttons depending on
   // the current mouse or touch event location, or if switch access is enabled.
   void UpdateHoverButtonsVisibility(const gfx::Point& screen_location,
diff --git a/ash/wm/desks/templates/desks_templates_test_util.h b/ash/wm/desks/templates/desks_templates_test_util.h
index c863710..74c8e2b 100644
--- a/ash/wm/desks/templates/desks_templates_test_util.h
+++ b/ash/wm/desks/templates/desks_templates_test_util.h
@@ -21,7 +21,6 @@
 
 namespace ash {
 
-class DesksTemplatesNameView;
 class DesksTemplatesPresenter;
 class RoundedImageView;
 class PillButton;
@@ -74,10 +73,6 @@
       const DesksTemplatesItemViewTestApi&) = delete;
   ~DesksTemplatesItemViewTestApi();
 
-  const DesksTemplatesNameView* name_view() const {
-    return item_view_->name_view_;
-  }
-
   const views::Label* time_view() const { return item_view_->time_view_; }
 
   const CloseButton* delete_button() const {
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index b9ce0eaf..3b078cb 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -585,9 +585,13 @@
                          return DesksTemplatesItemViewTestApi(v).uuid() == uuid;
                        });
       ASSERT_NE(grid_items.end(), iter);
-      DesksTemplatesItemViewTestApi test_api(*iter);
-      EXPECT_EQ(base::UTF8ToUTF16(name), test_api.name_view()->GetText());
-      EXPECT_FALSE(test_api.time_view()->GetText().empty());
+
+      DesksTemplatesItemView* item_view = *iter;
+      EXPECT_EQ(base::UTF8ToUTF16(name), item_view->name_view()->GetText());
+      EXPECT_FALSE(DesksTemplatesItemViewTestApi(item_view)
+                       .time_view()
+                       ->GetText()
+                       .empty());
     };
 
     verify_template_grid_item(uuid_1, name_1);
@@ -1074,9 +1078,17 @@
   SendKey(ui::VKEY_TAB);
   EXPECT_EQ(first_item, GetHighlightedView());
 
+  // Testing that we traverse to the `name_view` of the first item.
+  SendKey(ui::VKEY_TAB);
+  EXPECT_EQ(first_item->name_view(), GetHighlightedView());
+
   // When we're done with the first item, we'll go on to the second.
   SendKey(ui::VKEY_TAB);
   EXPECT_EQ(second_item, GetHighlightedView());
+
+  // Testing that we traverse to the `name_view` of the second item.
+  SendKey(ui::VKEY_TAB);
+  EXPECT_EQ(second_item->name_view(), GetHighlightedView());
 }
 
 // Tests that the desks bar returns to zero state if the second-to-last desk is
diff --git a/ash/wm/overview/overview_highlight_controller.cc b/ash/wm/overview/overview_highlight_controller.cc
index 94be8a3..c2d3ad1 100644
--- a/ash/wm/overview/overview_highlight_controller.cc
+++ b/ash/wm/overview/overview_highlight_controller.cc
@@ -16,6 +16,7 @@
 #include "ash/wm/desks/expanded_desks_bar_button.h"
 #include "ash/wm/desks/templates/desks_templates_grid_view.h"
 #include "ash/wm/desks/templates/desks_templates_item_view.h"
+#include "ash/wm/desks/templates/desks_templates_name_view.h"
 #include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/overview/overview_highlightable_view.h"
@@ -212,6 +213,7 @@
       for (DesksTemplatesItemView* template_item :
            templates_grid_view->grid_items()) {
         traversable_views.push_back(template_item);
+        traversable_views.push_back(template_item->name_view());
       }
     } else {
       for (auto& item : grid->window_list())
diff --git a/base/location.cc b/base/location.cc
index c0a2de0c..144f247 100644
--- a/base/location.cc
+++ b/base/location.cc
@@ -74,6 +74,7 @@
 
 Location::Location() = default;
 Location::Location(const Location& other) = default;
+Location::Location(Location&& other) noexcept = default;
 Location& Location::operator=(const Location& other) = default;
 
 Location::Location(const char* file_name, const void* program_counter)
diff --git a/base/location.h b/base/location.h
index 4c30d270..4461147 100644
--- a/base/location.h
+++ b/base/location.h
@@ -33,6 +33,7 @@
  public:
   Location();
   Location(const Location& other);
+  Location(Location&& other) noexcept;
   Location& operator=(const Location& other);
 
   // Only initializes the file name and program counter, the source information
diff --git a/base/task/default_delayed_task_handle_delegate.cc b/base/task/default_delayed_task_handle_delegate.cc
index f1d28981..63b952cc 100644
--- a/base/task/default_delayed_task_handle_delegate.cc
+++ b/base/task/default_delayed_task_handle_delegate.cc
@@ -12,6 +12,8 @@
 
 DefaultDelayedTaskHandleDelegate::DefaultDelayedTaskHandleDelegate() = default;
 
+DefaultDelayedTaskHandleDelegate::~DefaultDelayedTaskHandleDelegate() = default;
+
 bool DefaultDelayedTaskHandleDelegate::IsValid() const {
   return weak_ptr_factory_.HasWeakPtrs();
 }
@@ -27,8 +29,6 @@
                   weak_ptr_factory_.GetWeakPtr(), std::move(callback));
 }
 
-DefaultDelayedTaskHandleDelegate::~DefaultDelayedTaskHandleDelegate() = default;
-
 void DefaultDelayedTaskHandleDelegate::RunTask(OnceClosure user_task) {
   // Invalidate the weak pointer first so that the task handle is considered
   // invalid while running the task.
diff --git a/base/task/default_delayed_task_handle_delegate.h b/base/task/default_delayed_task_handle_delegate.h
index ae610d38..6095fa09 100644
--- a/base/task/default_delayed_task_handle_delegate.h
+++ b/base/task/default_delayed_task_handle_delegate.h
@@ -18,6 +18,7 @@
     : public DelayedTaskHandle::Delegate {
  public:
   DefaultDelayedTaskHandleDelegate();
+  ~DefaultDelayedTaskHandleDelegate() override;
 
   // DelayedTaskHandle::Delegate:
   bool IsValid() const override;
@@ -28,8 +29,6 @@
   OnceClosure BindCallback(OnceClosure callback);
 
  private:
-  ~DefaultDelayedTaskHandleDelegate() override;
-
   // Runs |callback|.
   void RunTask(OnceClosure callback);
 
diff --git a/base/task/default_delayed_task_handle_delegate_unittest.cc b/base/task/default_delayed_task_handle_delegate_unittest.cc
index 8ae5c44..72ad7dab 100644
--- a/base/task/default_delayed_task_handle_delegate_unittest.cc
+++ b/base/task/default_delayed_task_handle_delegate_unittest.cc
@@ -13,7 +13,7 @@
 
 // Tests that running the bound callback invalidates the handle.
 TEST(DefaultDelayedTaskHandleDelegateTest, RunTask) {
-  auto delegate = base::MakeRefCounted<DefaultDelayedTaskHandleDelegate>();
+  auto delegate = std::make_unique<DefaultDelayedTaskHandleDelegate>();
   EXPECT_FALSE(delegate->IsValid());
 
   auto bound_callback = delegate->BindCallback(DoNothing());
@@ -28,7 +28,7 @@
 
 // Tests that DefaultDelayedTaskHandleDelegate supports CancelTask().
 TEST(DefaultDelayedTaskHandleDelegateTest, CancelTask) {
-  auto delegate = base::MakeRefCounted<DefaultDelayedTaskHandleDelegate>();
+  auto delegate = std::make_unique<DefaultDelayedTaskHandleDelegate>();
   EXPECT_FALSE(delegate->IsValid());
 
   auto bound_callback = delegate->BindCallback(DoNothing());
@@ -44,7 +44,7 @@
 
 // Tests that destroying the bound callback invalidates the handle.
 TEST(DefaultDelayedTaskHandleDelegateTest, DestroyTask) {
-  auto delegate = base::MakeRefCounted<DefaultDelayedTaskHandleDelegate>();
+  auto delegate = std::make_unique<DefaultDelayedTaskHandleDelegate>();
   EXPECT_FALSE(delegate->IsValid());
 
   auto bound_callback = delegate->BindCallback(DoNothing());
@@ -59,7 +59,7 @@
 
 // Tests that the handle is invalid while the task is running.
 TEST(DefaultDelayedTaskHandleDelegateTest, HandleInvalidInsideCallback) {
-  auto delegate = base::MakeRefCounted<DefaultDelayedTaskHandleDelegate>();
+  auto delegate = std::make_unique<DefaultDelayedTaskHandleDelegate>();
   EXPECT_FALSE(delegate->IsValid());
 
   auto bound_callback = delegate->BindCallback(
diff --git a/base/task/delayed_task_handle.cc b/base/task/delayed_task_handle.cc
index b9194ec..bd0c370 100644
--- a/base/task/delayed_task_handle.cc
+++ b/base/task/delayed_task_handle.cc
@@ -12,7 +12,7 @@
 
 DelayedTaskHandle::DelayedTaskHandle() = default;
 
-DelayedTaskHandle::DelayedTaskHandle(scoped_refptr<Delegate> delegate)
+DelayedTaskHandle::DelayedTaskHandle(std::unique_ptr<Delegate> delegate)
     : delegate_(std::move(delegate)) {
   DCHECK(IsValid());
 }
diff --git a/base/task/delayed_task_handle.h b/base/task/delayed_task_handle.h
index f2d4003..b02dca94 100644
--- a/base/task/delayed_task_handle.h
+++ b/base/task/delayed_task_handle.h
@@ -5,8 +5,9 @@
 #ifndef BASE_TASK_DELAYED_TASK_HANDLE_H_
 #define BASE_TASK_DELAYED_TASK_HANDLE_H_
 
+#include <memory>
+
 #include "base/base_export.h"
-#include "base/memory/ref_counted.h"
 
 namespace base {
 
@@ -16,25 +17,23 @@
  public:
   // The delegate that allows each SequencedTaskRunners to have different
   // implementations.
-  class Delegate : public RefCounted<Delegate> {
+  class Delegate {
    public:
+    virtual ~Delegate() = default;
+
     // Returns true if the task handle is valid.
     virtual bool IsValid() const = 0;
 
     // Cancels the task. A canceled task, whether removed from the underlying
     // queue or only marked as canceled, will never be Run().
     virtual void CancelTask() = 0;
-
-   protected:
-    friend class RefCounted<Delegate>;
-    virtual ~Delegate() = default;
   };
 
   // Construct a default, invalid, task handle.
   DelayedTaskHandle();
 
   // Construct a valid task handle with the specified |delegate|.
-  explicit DelayedTaskHandle(scoped_refptr<Delegate> delegate);
+  explicit DelayedTaskHandle(std::unique_ptr<Delegate> delegate);
 
   ~DelayedTaskHandle();
 
@@ -48,7 +47,7 @@
   void CancelTask();
 
  private:
-  scoped_refptr<Delegate> delegate_;
+  std::unique_ptr<Delegate> delegate_;
 };
 
 }  // namespace base
diff --git a/base/task/delayed_task_handle_unittest.cc b/base/task/delayed_task_handle_unittest.cc
index 6d53c86c..05dbd1c 100644
--- a/base/task/delayed_task_handle_unittest.cc
+++ b/base/task/delayed_task_handle_unittest.cc
@@ -15,13 +15,12 @@
 class TestDelegate : public DelayedTaskHandle::Delegate {
  public:
   TestDelegate() = default;
+  ~TestDelegate() override = default;
 
   bool IsValid() const override { return is_valid_; }
   void CancelTask() override { is_valid_ = false; }
 
  private:
-  ~TestDelegate() override = default;
-
   // Indicates if this delegate is currently valid.
   bool is_valid_ = true;
 };
@@ -36,7 +35,7 @@
 
 // Tests that creating a handle with an invalid delegate will DCHECK.
 TEST(DelayedTaskHandleTest, RequiresValidDelegateOnConstruction) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
   // Invalidate the delegate before creating the handle.
@@ -50,16 +49,17 @@
 // Tests that calling CancelTask() on the handle will call CancelTask() on the
 // delegate and invalidate it.
 TEST(DelayedTaskHandleTest, CancelTask) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
   delayed_task_handle.CancelTask();
 
-  EXPECT_FALSE(delegate->IsValid());
+  EXPECT_FALSE(delegate_ptr->IsValid());
   EXPECT_FALSE(delayed_task_handle.IsValid());
 }
 
@@ -75,62 +75,66 @@
 // Tests that calling CancelTask() on a handle with an invalid delegate will
 // no-op.
 TEST(DelayedTaskHandleTest, CancelTaskInvalidDelegate) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
-  delegate->CancelTask();
+  delegate_ptr->CancelTask();
 
-  EXPECT_FALSE(delegate->IsValid());
+  EXPECT_FALSE(delegate_ptr->IsValid());
   EXPECT_FALSE(delayed_task_handle.IsValid());
 
   delayed_task_handle.CancelTask();
 
-  EXPECT_FALSE(delegate->IsValid());
+  EXPECT_FALSE(delegate_ptr->IsValid());
   EXPECT_FALSE(delayed_task_handle.IsValid());
 }
 
 // Tests that invalidating the delegate will also invalidate the handle.
 TEST(DelayedTaskHandleTest, InvalidateDelegate) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
-  delegate->CancelTask();
+  delegate_ptr->CancelTask();
 
-  EXPECT_FALSE(delegate->IsValid());
+  EXPECT_FALSE(delegate_ptr->IsValid());
   EXPECT_FALSE(delayed_task_handle.IsValid());
 }
 
 // Tests that destroying a valid handle will DCHECK.
 TEST(DelayedTaskHandleTest, InvalidOnDestuction) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
+  auto* delegate_ptr = delegate.get();
   EXPECT_DCHECK_DEATH({
-    DelayedTaskHandle delayed_task_handle(delegate);
-    EXPECT_TRUE(delegate->IsValid());
+    DelayedTaskHandle delayed_task_handle(std::move(delegate));
+    EXPECT_TRUE(delegate_ptr->IsValid());
     EXPECT_TRUE(delayed_task_handle.IsValid());
   });
 }
 
 // Tests the move-constructor.
 TEST(DelayedTaskHandleTest, MoveConstructor) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
   DelayedTaskHandle other_delayed_task_handle(std::move(delayed_task_handle));
-  EXPECT_TRUE(delegate->IsValid());
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(other_delayed_task_handle.IsValid());
 
   // Clean-up.
@@ -139,18 +143,19 @@
 
 // Tests the move-assignment.
 TEST(DelayedTaskHandleTest, MoveAssignment) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
   DelayedTaskHandle other_delayed_task_handle;
   EXPECT_FALSE(other_delayed_task_handle.IsValid());
 
   other_delayed_task_handle = std::move(delayed_task_handle);
-  EXPECT_TRUE(delegate->IsValid());
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(other_delayed_task_handle.IsValid());
 
   // Clean-up.
@@ -159,11 +164,12 @@
 
 // Tests that assigning to a valid handle will DCHECK.
 TEST(DelayedTaskHandleTest, AssignToValidHandle) {
-  auto delegate = base::MakeRefCounted<TestDelegate>();
+  auto delegate = std::make_unique<TestDelegate>();
   EXPECT_TRUE(delegate->IsValid());
 
-  DelayedTaskHandle delayed_task_handle(delegate);
-  EXPECT_TRUE(delegate->IsValid());
+  auto* delegate_ptr = delegate.get();
+  DelayedTaskHandle delayed_task_handle(std::move(delegate));
+  EXPECT_TRUE(delegate_ptr->IsValid());
   EXPECT_TRUE(delayed_task_handle.IsValid());
 
   EXPECT_DCHECK_DEATH({ delayed_task_handle = DelayedTaskHandle(); });
diff --git a/base/task/sequenced_task_runner.cc b/base/task/sequenced_task_runner.cc
index b75cfeb..2132f05 100644
--- a/base/task/sequenced_task_runner.cc
+++ b/base/task/sequenced_task_runner.cc
@@ -22,7 +22,7 @@
     OnceClosure task,
     TimeDelta delay) {
   auto delayed_task_handle_delegate =
-      base::MakeRefCounted<DefaultDelayedTaskHandleDelegate>();
+      std::make_unique<DefaultDelayedTaskHandleDelegate>();
 
   task = delayed_task_handle_delegate->BindCallback(std::move(task));
 
diff --git a/build/android/gyp/create_unwind_table.py b/build/android/gyp/create_unwind_table.py
index 5af3403..8c9c347f 100755
--- a/build/android/gyp/create_unwind_table.py
+++ b/build/android/gyp/create_unwind_table.py
@@ -5,14 +5,20 @@
 """Creates a table of unwind information in Android Chrome's bespoke format."""
 
 import abc
+import argparse
+import collections
 import enum
 import logging
+import os
 import re
-import collections
 import struct
+import subprocess
+import sys
 from typing import (Dict, Iterable, List, NamedTuple, Sequence, TextIO, Tuple,
                     Union)
 
+from util import build_utils
+
 _STACK_CFI_INIT_REGEX = re.compile(
     r'^STACK CFI INIT ([0-9a-f]+) ([0-9a-f]+) (.+)$')
 _STACK_CFI_REGEX = re.compile(r'^STACK CFI ([0-9a-f]+) (.+)$')
@@ -653,6 +659,7 @@
   text_section_start_address: int = sorted_function_unwinds[0].address
   prev_func_end_address: int = sorted_function_unwinds[0].address
 
+  gaps = 0
   for unwind in sorted_function_unwinds:
     assert prev_func_end_address <= unwind.address, (
         'Detected overlap between functions.')
@@ -661,6 +668,7 @@
       # Gaps between functions are typically filled by regions of thunks which
       # do not alter the stack pointer. Filling these gaps with TRIVIAL_UNWIND
       # is the appropriate unwind strategy.
+      gaps += 1
       yield EncodedFunctionUnwind(GetPageNumber(prev_func_end_address),
                                   GetPageOffset(prev_func_end_address),
                                   TRIVIAL_UNWIND)
@@ -676,6 +684,9 @@
                                 GetPageOffset(prev_func_end_address),
                                 REFUSE_TO_UNWIND)
 
+  logging.info('%d/%d gaps between functions filled with trivial unwind.', gaps,
+               len(sorted_function_unwinds))
+
 
 def EncodeFunctionOffsetTable(
     encoded_address_unwind_sequences: Iterable[
@@ -869,8 +880,8 @@
         epilogues_seen += 1
         break
 
-      logging.info('unrecognized CFI: %x %s.' %
-                   (address_cfi.address, address_cfi.unwind_instructions))
+      logging.info('unrecognized CFI: %x %s.', address_cfi.address,
+                   address_cfi.unwind_instructions)
 
     if address_unwinds:
       # We expect that the unwind information for every function starts with a
@@ -996,3 +1007,47 @@
 
   return (page_table, function_table, function_offset_table,
           unwind_instruction_table)
+
+
+def main():
+  build_utils.InitLogging('CREATE_UNWIND_TABLE_DEBUG')
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--input_path',
+                      help='Path to the unstripped binary.',
+                      required=True,
+                      metavar='FILE')
+  parser.add_argument('--output_path',
+                      help='Path to unwind info binary output.',
+                      required=True,
+                      metavar='FILE')
+  parser.add_argument('--dump_syms_path',
+                      required=True,
+                      help='The path of the dump_syms binary.',
+                      metavar='FILE')
+
+  args = parser.parse_args()
+  proc = subprocess.Popen(['./' + args.dump_syms_path, args.input_path, '-v'],
+                          stdout=subprocess.PIPE,
+                          encoding='ascii')
+
+  function_cfis = ReadFunctionCfi(proc.stdout)
+  function_unwinds = GenerateUnwinds(function_cfis, parsers=ALL_PARSERS)
+  encoded_function_unwinds = EncodeFunctionUnwinds(function_unwinds)
+  (page_table, function_table, function_offset_table,
+   unwind_instruction_table) = GenerateUnwindTables(encoded_function_unwinds)
+  unwind_info: bytes = EncodeUnwindInfo(page_table, function_table,
+                                        function_offset_table,
+                                        unwind_instruction_table)
+
+  if proc.wait():
+    logging.critical('dump_syms exited with return code %d', proc.returncode)
+    sys.exit(proc.returncode)
+
+  with open(args.output_path, 'wb') as f:
+    f.write(unwind_info)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/config/android/create_unwind_table.gni b/build/config/android/create_unwind_table.gni
new file mode 100644
index 0000000..15f8fd9
--- /dev/null
+++ b/build/config/android/create_unwind_table.gni
@@ -0,0 +1,44 @@
+# 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.
+
+import("//build/config/android/rules.gni")
+
+template("unwind_table_asset_v2") {
+  _asset_path = "${target_out_dir}/${target_name}/unwind_cfi_32_v2"
+  _unwind_action = "${target_name}__create_unwind_table"
+
+  action(_unwind_action) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+
+    _root_dir = "$root_out_dir"
+    if (defined(android_secondary_abi_cpu)) {
+      _root_dir = get_label_info(":foo($android_secondary_abi_toolchain)",
+                                 "root_out_dir")
+    }
+
+    script = "//build/android/gyp/create_unwind_table.py"
+    outputs = [ _asset_path ]
+    inputs = [ "${_root_dir}/lib.unstripped/$shlib_prefix${invoker.library_target}$shlib_extension" ]
+
+    args = [
+      "--input_path",
+      rebase_path(
+          "${_root_dir}/lib.unstripped/$shlib_prefix${invoker.library_target}$shlib_extension",
+          root_build_dir),
+      "--output_path",
+      rebase_path(_asset_path, root_build_dir),
+      "--dump_syms_path",
+      rebase_path("$root_out_dir/dump_syms", root_build_dir),
+    ]
+    deps = invoker.deps
+    deps += [ "//third_party/breakpad:dump_syms" ]
+  }
+
+  android_assets(target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    sources = [ _asset_path ]
+    disable_compression = true
+    deps = [ ":$_unwind_action" ]
+  }
+}
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index f3c9f9f..a41df19 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -204,6 +204,9 @@
 enable_arm_cfi_table = is_android && !is_component_build && current_cpu == "arm"
 
 declare_args() {
+  # Set to true to use the android unwinder V2 implementation.
+  use_android_unwinder_v2 = false
+
   # If this running on a GPU FYI bot.
   # TODO(https://crbug.com/1233871): Remove this again.
   is_gpu_fyi_bot = false
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 57418ae..f8fd0041 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -420,6 +420,7 @@
         '--user-data-dir=%s' % tmp_ash_data_dir_name,
         '--enable-wayland-server',
         '--no-startup-window',
+        '--enable-features=LacrosSupport',
         '--ash-ready-file-path=%s' % ash_ready_file,
     ]
     if enable_mojo_crosapi:
diff --git a/build/lacros/test_runner_test.py b/build/lacros/test_runner_test.py
index 8fa3220..d71a8bb 100755
--- a/build/lacros/test_runner_test.py
+++ b/build/lacros/test_runner_test.py
@@ -79,7 +79,7 @@
           'build/lacros/prebuilt_ash_chrome/793554/test_ash_chrome'))
       expected_ash_chrome_args = [
           '--user-data-dir=/tmp/ash-data', '--enable-wayland-server',
-          '--no-startup-window',
+          '--no-startup-window', '--enable-features=LacrosSupport',
           '--ash-ready-file-path=/tmp/ash-data/ash_ready.txt'
       ]
       if command == 'lacros_chrome_browsertests':
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc
index 12d589cd0..bec59290 100644
--- a/build/sanitizers/lsan_suppressions.cc
+++ b/build/sanitizers/lsan_suppressions.cc
@@ -79,6 +79,8 @@
     "leak:ash::LockStateController::StartPostLockAnimation\n"
     // Suppress leak in SurfaceDrawContext. crbug.com/1265033
     "leak:skgpu::v1::SurfaceDrawContext::drawGlyphRunListWithCache\n"
+    // Suppress leak in PagedAppsGridView. crbug.com/1276658
+    "leak:ash::PagedAppsGridView::AnimateCardifiedState\n"
 #endif
 
     // PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS.
diff --git a/cc/layers/heads_up_display_layer.cc b/cc/layers/heads_up_display_layer.cc
index 41fd7b4..9af25650 100644
--- a/cc/layers/heads_up_display_layer.cc
+++ b/cc/layers/heads_up_display_layer.cc
@@ -91,9 +91,11 @@
   web_vital_metrics_ = std::move(web_vital_metrics);
 }
 
-void HeadsUpDisplayLayer::PushPropertiesTo(LayerImpl* layer,
-                                           const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void HeadsUpDisplayLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
   TRACE_EVENT0("cc", "HeadsUpDisplayLayer::PushPropertiesTo");
   HeadsUpDisplayLayerImpl* layer_impl =
       static_cast<HeadsUpDisplayLayerImpl*>(layer);
diff --git a/cc/layers/heads_up_display_layer.h b/cc/layers/heads_up_display_layer.h
index 1b5cf52..ff42893 100644
--- a/cc/layers/heads_up_display_layer.h
+++ b/cc/layers/heads_up_display_layer.h
@@ -37,7 +37,8 @@
 
   // Layer overrides.
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
  protected:
   HeadsUpDisplayLayer();
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 78f51d80..6fb3520 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1184,15 +1184,25 @@
   SetNeedsPushProperties();
 }
 
-int Layer::transform_tree_index() const {
-  if (!layer_tree_host_ ||
-      layer_tree_host_->property_trees()->sequence_number !=
-          property_tree_sequence_number_) {
+int Layer::transform_tree_index(const PropertyTrees& property_trees) const {
+  if (property_trees.sequence_number != property_tree_sequence_number_) {
     return TransformTree::kInvalidNodeId;
   }
   return transform_tree_index_;
 }
 
+bool Layer::transform_tree_index_is_valid(
+    const PropertyTrees& property_trees) const {
+  return transform_tree_index_ != TransformTree::kInvalidNodeId &&
+         property_trees.sequence_number == property_tree_sequence_number_;
+}
+
+int Layer::transform_tree_index() const {
+  if (!IsAttached())
+    return TransformTree::kInvalidNodeId;
+  return transform_tree_index(*layer_tree_host()->property_trees());
+}
+
 void Layer::SetClipTreeIndex(int index) {
   DCHECK(IsPropertyChangeAllowed());
   if (clip_tree_index_ == index)
@@ -1201,15 +1211,25 @@
   SetNeedsPushProperties();
 }
 
-int Layer::clip_tree_index() const {
-  if (!layer_tree_host_ ||
-      layer_tree_host_->property_trees()->sequence_number !=
-          property_tree_sequence_number_) {
+int Layer::clip_tree_index(const PropertyTrees& property_trees) const {
+  if (property_trees.sequence_number != property_tree_sequence_number_) {
     return ClipTree::kInvalidNodeId;
   }
   return clip_tree_index_;
 }
 
+bool Layer::clip_tree_index_is_valid(
+    const PropertyTrees& property_trees) const {
+  return clip_tree_index_ != ClipTree::kInvalidNodeId &&
+         property_trees.sequence_number == property_tree_sequence_number_;
+}
+
+int Layer::clip_tree_index() const {
+  if (!IsAttached())
+    return ClipTree::kInvalidNodeId;
+  return clip_tree_index(*layer_tree_host()->property_trees());
+}
+
 void Layer::SetEffectTreeIndex(int index) {
   DCHECK(IsPropertyChangeAllowed());
   if (effect_tree_index_ == index)
@@ -1218,15 +1238,25 @@
   SetNeedsPushProperties();
 }
 
-int Layer::effect_tree_index() const {
-  if (!layer_tree_host_ ||
-      layer_tree_host_->property_trees()->sequence_number !=
-          property_tree_sequence_number_) {
+int Layer::effect_tree_index(const PropertyTrees& property_trees) const {
+  if (property_trees.sequence_number != property_tree_sequence_number_) {
     return EffectTree::kInvalidNodeId;
   }
   return effect_tree_index_;
 }
 
+bool Layer::effect_tree_index_is_valid(
+    const PropertyTrees& property_trees) const {
+  return effect_tree_index_ != EffectTree::kInvalidNodeId &&
+         property_trees.sequence_number == property_tree_sequence_number_;
+}
+
+int Layer::effect_tree_index() const {
+  if (!IsAttached())
+    return EffectTree::kInvalidNodeId;
+  return effect_tree_index(*layer_tree_host()->property_trees());
+}
+
 void Layer::SetScrollTreeIndex(int index) {
   DCHECK(IsPropertyChangeAllowed());
   if (scroll_tree_index_ == index)
@@ -1235,15 +1265,25 @@
   SetNeedsPushProperties();
 }
 
-int Layer::scroll_tree_index() const {
-  if (!layer_tree_host_ ||
-      layer_tree_host_->property_trees()->sequence_number !=
-          property_tree_sequence_number_) {
+int Layer::scroll_tree_index(const PropertyTrees& property_trees) const {
+  if (property_trees.sequence_number != property_tree_sequence_number_) {
     return ScrollTree::kInvalidNodeId;
   }
   return scroll_tree_index_;
 }
 
+bool Layer::scroll_tree_index_is_valid(
+    const PropertyTrees& property_trees) const {
+  return scroll_tree_index_ != ScrollTree::kInvalidNodeId &&
+         property_trees.sequence_number == property_tree_sequence_number_;
+}
+
+int Layer::scroll_tree_index() const {
+  if (!IsAttached())
+    return ScrollTree::kInvalidNodeId;
+  return scroll_tree_index(*layer_tree_host()->property_trees());
+}
+
 void Layer::SetOffsetToTransformParent(gfx::Vector2dF offset) {
   if (offset_to_transform_parent_ == offset)
     return;
@@ -1350,11 +1390,14 @@
 }
 
 void Layer::PushPropertiesTo(LayerImpl* layer,
-                             const CommitState& commit_state) {
+                             const CommitState& commit_state,
+                             const ThreadUnsafeCommitState& unsafe_state) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                "Layer::PushPropertiesTo");
   DCHECK(layer_tree_host_);
 
+  const PropertyTrees& property_trees = unsafe_state.property_trees;
+
   // The element id should be set first because other setters may
   // depend on it. Referencing element id on a layer is
   // deprecated. http://crbug.com/709137
@@ -1364,10 +1407,10 @@
   layer->SetSafeOpaqueBackgroundColor(
       SafeOpaqueBackgroundColor(commit_state.background_color));
   layer->SetBounds(inputs_.bounds);
-  layer->SetTransformTreeIndex(transform_tree_index());
-  layer->SetEffectTreeIndex(effect_tree_index());
-  layer->SetClipTreeIndex(clip_tree_index());
-  layer->SetScrollTreeIndex(scroll_tree_index());
+  layer->SetTransformTreeIndex(transform_tree_index(property_trees));
+  layer->SetEffectTreeIndex(effect_tree_index(property_trees));
+  layer->SetClipTreeIndex(clip_tree_index(property_trees));
+  layer->SetScrollTreeIndex(scroll_tree_index(property_trees));
   layer->SetOffsetToTransformParent(offset_to_transform_parent_);
   layer->SetDrawsContent(DrawsContent());
   layer->SetHitTestable(HitTestable());
@@ -1394,7 +1437,7 @@
   // the pending tree will clobber any impl-side scrolling occuring on the
   // active tree. To do so, avoid scrolling the pending tree along with it
   // instead of trying to undo that scrolling later.
-  if (layer_tree_host_->mutator_host()->ScrollOffsetAnimationWasInterrupted(
+  if (unsafe_state.mutator_host->ScrollOffsetAnimationWasInterrupted(
           element_id())) {
     PropertyTrees* trees = layer->layer_tree_impl()->property_trees();
     trees->scroll_tree.SetScrollOffsetClobberActiveValue(layer->element_id());
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 06b3972..069f627 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -54,6 +54,7 @@
 class PictureLayer;
 
 struct CommitState;
+struct ThreadUnsafeCommitState;
 
 // For tracing and debugging. The info will be attached to this layer's tracing
 // output.
@@ -132,6 +133,8 @@
   // The list of children of this layer.
   const LayerList& children() const { return inputs_.children; }
 
+  bool IsAttached() const { return layer_tree_host_; }
+
   // Gets the LayerTreeHost that this layer is attached to, or null if not.
   // A layer is attached to a LayerTreeHost if it or an ancestor layer is set as
   // the root layer of a LayerTreeHost (while noting only a layer without a
@@ -572,6 +575,11 @@
   int effect_tree_index() const;
   int scroll_tree_index() const;
 
+  bool transform_tree_index_is_valid(const PropertyTrees&) const;
+  bool clip_tree_index_is_valid(const PropertyTrees&) const;
+  bool effect_tree_index_is_valid(const PropertyTrees&) const;
+  bool scroll_tree_index_is_valid(const PropertyTrees&) const;
+
   // While all layers have an index into the transform tree, this value
   // indicates whether the transform tree node was created for this layer.
   void SetHasTransformNode(bool val) { has_transform_node_ = val; }
@@ -644,7 +652,8 @@
   // CreateLayerImpl(), so can be safely down-casted if the subclass uses a
   // different type for the compositor thread.
   virtual void PushPropertiesTo(LayerImpl* layer,
-                                const CommitState& commit_state);
+                                const CommitState& commit_state,
+                                const ThreadUnsafeCommitState& unsafe_state);
 
   // Internal method to be overridden by Layer subclasses that need to do work
   // during a main frame. The method should compute any state that will need to
@@ -855,6 +864,11 @@
 
   void SetMirrorCount(int mirror_count);
 
+  int transform_tree_index(const PropertyTrees&) const;
+  int clip_tree_index(const PropertyTrees&) const;
+  int effect_tree_index(const PropertyTrees&) const;
+  int scroll_tree_index(const PropertyTrees&) const;
+
   // Encapsulates all data, callbacks or interfaces received from the embedder.
   struct Inputs {
     explicit Inputs(int layer_id);
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc
index 5a8849df..4c419ecb 100644
--- a/cc/layers/layer_perftest.cc
+++ b/cc/layers/layer_perftest.cc
@@ -89,8 +89,9 @@
     // layer_tree_host_->ActivateCommitState() and the second argument would
     // come from layer_tree_host_->active_commit_state(); we use
     // pending_commit_state() just to keep the test code simple.
-    test_layer->PushPropertiesTo(impl_layer.get(),
-                                 *layer_tree_host_->GetPendingCommitState());
+    test_layer->PushPropertiesTo(
+        impl_layer.get(), *layer_tree_host_->GetPendingCommitState(),
+        layer_tree_host_->GetThreadUnsafeCommitState());
 
     transform_origin_z += 0.01f;
     scrollable = !scrollable;
@@ -108,8 +109,9 @@
   // Properties didn't change.
   timer_.Reset();
   do {
-    test_layer->PushPropertiesTo(impl_layer.get(),
-                                 *layer_tree_host_->GetPendingCommitState());
+    test_layer->PushPropertiesTo(
+        impl_layer.get(), *layer_tree_host_->GetPendingCommitState(),
+        layer_tree_host_->GetThreadUnsafeCommitState());
     timer_.NextLap();
   } while (!timer_.HasTimeLimitExpired());
 
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 9dd45b42..5ae59581 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -168,13 +168,15 @@
   void SimulateCommitForLayer(Layer* layer) {
     layer->PushPropertiesTo(
         layer->CreateLayerImpl(host_impl_.active_tree()).get(),
-        *layer_tree_host_->GetPendingCommitState());
+        *layer_tree_host_->GetPendingCommitState(),
+        layer_tree_host_->GetThreadUnsafeCommitState());
   }
 
   void CommitAndPushProperties(Layer* layer, LayerImpl* layer_impl) {
+    auto& unsafe_state = layer_tree_host_->GetThreadUnsafeCommitState();
     std::unique_ptr<CommitState> commit_state = layer_tree_host_->WillCommit(
         /*completion=*/nullptr, /*has_updates=*/true);
-    layer->PushPropertiesTo(layer_impl, *commit_state);
+    layer->PushPropertiesTo(layer_impl, *commit_state, unsafe_state);
     layer_tree_host_->CommitComplete(
         {base::TimeTicks(), base::TimeTicks::Now()});
   }
@@ -318,14 +320,17 @@
   host_impl_.active_tree()->set_source_frame_number(
       host_impl_.active_tree()->source_frame_number() + 1);
 
+  auto& unsafe_state = layer_tree_host_->GetThreadUnsafeCommitState();
   std::unique_ptr<CommitState> commit_state = layer_tree_host_->WillCommit(
       /*completion=*/nullptr, /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state);
-      mask_layer1->PushPropertiesTo(mask_layer1_impl.get(), *commit_state););
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state);
+      mask_layer1->PushPropertiesTo(mask_layer1_impl.get(), *commit_state,
+                                    unsafe_state););
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   // Once there is a mask layer, resizes require subtree properties to update.
@@ -340,10 +345,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
@@ -352,10 +358,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
@@ -364,10 +371,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
@@ -376,10 +384,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2);
@@ -389,10 +398,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
@@ -401,10 +411,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
@@ -413,10 +424,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   // Should be a different size than previous call, to ensure it marks tree
@@ -429,10 +441,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   FilterOperations arbitrary_filters;
@@ -443,10 +456,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2);
@@ -456,10 +470,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state));
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state));
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   gfx::PointF arbitrary_point_f = gfx::PointF(0.125f, 0.25f);
@@ -472,10 +487,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state);
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state);
       layer_tree_host_->property_trees()->ResetAllChangeTracking());
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
   EXPECT_FALSE(node->transform_changed);
@@ -489,8 +505,9 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state);
       layer_tree_host_->property_trees()->ResetAllChangeTracking());
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
   node = layer_tree_host_->property_trees()->transform_tree.Node(
@@ -507,10 +524,11 @@
   commit_state = layer_tree_host_->WillCommit(/*completion=*/nullptr,
                                               /*has_updates=*/true);
   EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET(
-      top->PushPropertiesTo(top_impl.get(), *commit_state);
-      child->PushPropertiesTo(child_impl.get(), *commit_state);
-      child2->PushPropertiesTo(child2_impl.get(), *commit_state);
-      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state);
+      top->PushPropertiesTo(top_impl.get(), *commit_state, unsafe_state);
+      child->PushPropertiesTo(child_impl.get(), *commit_state, unsafe_state);
+      child2->PushPropertiesTo(child2_impl.get(), *commit_state, unsafe_state);
+      grand_child->PushPropertiesTo(grand_child_impl.get(), *commit_state,
+                                    unsafe_state);
       layer_tree_host_->property_trees()->ResetAllChangeTracking());
   layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
diff --git a/cc/layers/mirror_layer.cc b/cc/layers/mirror_layer.cc
index 26db296..82e421f 100644
--- a/cc/layers/mirror_layer.cc
+++ b/cc/layers/mirror_layer.cc
@@ -15,9 +15,11 @@
   return MirrorLayerImpl::Create(tree_impl, id());
 }
 
-void MirrorLayer::PushPropertiesTo(LayerImpl* layer,
-                                   const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void MirrorLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
 
   auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
   mirror_layer->SetMirroredLayerId(mirrored_layer_->id());
diff --git a/cc/layers/mirror_layer.h b/cc/layers/mirror_layer.h
index ff5b473..6a875a9 100644
--- a/cc/layers/mirror_layer.h
+++ b/cc/layers/mirror_layer.h
@@ -26,7 +26,8 @@
   // Layer overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
 
  protected:
diff --git a/cc/layers/nine_patch_layer.cc b/cc/layers/nine_patch_layer.cc
index fac969a..57cad735 100644
--- a/cc/layers/nine_patch_layer.cc
+++ b/cc/layers/nine_patch_layer.cc
@@ -65,9 +65,11 @@
   SetNeedsCommit();
 }
 
-void NinePatchLayer::PushPropertiesTo(LayerImpl* layer,
-                                      const CommitState& commit_state) {
-  UIResourceLayer::PushPropertiesTo(layer, commit_state);
+void NinePatchLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  UIResourceLayer::PushPropertiesTo(layer, commit_state, unsafe_state);
   TRACE_EVENT0("cc", "NinePatchLayer::PushPropertiesTo");
   NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
 
diff --git a/cc/layers/nine_patch_layer.h b/cc/layers/nine_patch_layer.h
index 079043f..95ebaf1 100644
--- a/cc/layers/nine_patch_layer.h
+++ b/cc/layers/nine_patch_layer.h
@@ -23,7 +23,8 @@
   NinePatchLayer& operator=(const NinePatchLayer&) = delete;
 
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   // |border| is the space around the center rectangular region in layer space
   // (known as aperture in image space).  |border.x()| and |border.y()| are the
diff --git a/cc/layers/painted_overlay_scrollbar_layer.cc b/cc/layers/painted_overlay_scrollbar_layer.cc
index a93d8cd..c28b115 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.cc
+++ b/cc/layers/painted_overlay_scrollbar_layer.cc
@@ -64,8 +64,9 @@
 
 void PaintedOverlayScrollbarLayer::PushPropertiesTo(
     LayerImpl* layer,
-    const CommitState& commit_state) {
-  ScrollbarLayerBase::PushPropertiesTo(layer, commit_state);
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  ScrollbarLayerBase::PushPropertiesTo(layer, commit_state, unsafe_state);
 
   PaintedOverlayScrollbarLayerImpl* scrollbar_layer =
       static_cast<PaintedOverlayScrollbarLayerImpl*>(layer);
diff --git a/cc/layers/painted_overlay_scrollbar_layer.h b/cc/layers/painted_overlay_scrollbar_layer.h
index 48374d8..0178d52 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.h
+++ b/cc/layers/painted_overlay_scrollbar_layer.h
@@ -37,7 +37,8 @@
   bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   ScrollbarLayerType GetScrollbarLayerType() const override;
 
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index cd98bfc..ba4a40a8 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -55,9 +55,11 @@
   return is_overlay_;
 }
 
-void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer,
-                                             const CommitState& commit_state) {
-  ScrollbarLayerBase::PushPropertiesTo(layer, commit_state);
+void PaintedScrollbarLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  ScrollbarLayerBase::PushPropertiesTo(layer, commit_state, unsafe_state);
 
   PaintedScrollbarLayerImpl* scrollbar_layer =
       static_cast<PaintedScrollbarLayerImpl*>(layer);
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index aec07ab..1b3f0f38 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -36,7 +36,8 @@
   bool Update() override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   const gfx::Size& internal_content_bounds() const {
     return internal_content_bounds_;
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 1771ad3..455d8a7 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -51,14 +51,16 @@
   return PictureLayerImpl::Create(tree_impl, id());
 }
 
-void PictureLayer::PushPropertiesTo(LayerImpl* base_layer,
-                                    const CommitState& commit_state) {
+void PictureLayer::PushPropertiesTo(
+    LayerImpl* base_layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
   // TODO(enne): http://crbug.com/918126 debugging
   CHECK(this);
 
   PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
 
-  Layer::PushPropertiesTo(base_layer, commit_state);
+  Layer::PushPropertiesTo(base_layer, commit_state, unsafe_state);
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                "PictureLayer::PushPropertiesTo");
   DropRecordingSourceContentIfInvalid(
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index 93e9618..05bfc3c 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -43,7 +43,8 @@
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
   void SetNeedsDisplayRect(const gfx::Rect& layer_rect) override;
   sk_sp<const SkPicture> GetPicture() const override;
   bool Update() override;
diff --git a/cc/layers/picture_layer_unittest.cc b/cc/layers/picture_layer_unittest.cc
index dab29d9..9867847 100644
--- a/cc/layers/picture_layer_unittest.cc
+++ b/cc/layers/picture_layer_unittest.cc
@@ -76,7 +76,8 @@
   // layer_tree_host_->ActivateCommitState() and the second argument would come
   // from layer_tree_host_->active_commit_state(); we use pending_commit_state()
   // just to keep the test code simple.
-  layer->PushPropertiesTo(layer_impl.get(), *host->GetPendingCommitState());
+  layer->PushPropertiesTo(layer_impl.get(), *host->GetPendingCommitState(),
+                          host->GetThreadUnsafeCommitState());
   EXPECT_FALSE(layer_impl->CanHaveTilings());
   EXPECT_TRUE(layer_impl->bounds() == gfx::Size(0, 0));
   EXPECT_EQ(gfx::Size(), layer_impl->raster_source()->GetSize());
@@ -117,7 +118,8 @@
       FakePictureLayerImpl::Create(host_impl.pending_tree(), 1));
   FakePictureLayerImpl* layer_impl = static_cast<FakePictureLayerImpl*>(
       host_impl.pending_tree()->root_layer());
-  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState());
+  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState(),
+                          host->GetThreadUnsafeCommitState());
 
   EXPECT_EQ(invalidation_bounds,
             layer_impl->GetPendingInvalidation()->bounds());
@@ -157,7 +159,8 @@
       FakePictureLayerImpl::Create(host_impl.pending_tree(), 1));
   FakePictureLayerImpl* layer_impl = static_cast<FakePictureLayerImpl*>(
       host_impl.pending_tree()->root_layer());
-  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState());
+  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState(),
+                          host->GetThreadUnsafeCommitState());
 
   EXPECT_EQ(gfx::Rect(), layer_impl->GetPendingInvalidation()->bounds());
 }
@@ -204,9 +207,10 @@
   SetupRootProperties(layer_impl);
   UpdateDrawProperties(host_impl.pending_tree());
 
+  const auto& unsafe_state = host->GetThreadUnsafeCommitState();
   std::unique_ptr<CommitState> commit_state =
       host->WillCommit(/*completion=*/nullptr, /*has_updates=*/true);
-  layer->PushPropertiesTo(layer_impl, *commit_state);
+  layer->PushPropertiesTo(layer_impl, *commit_state, unsafe_state);
   host->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
 
   EXPECT_EQ(2, host->SourceFrameNumber());
@@ -225,7 +229,8 @@
 
   // We should now have invalid contents and should therefore clear the
   // recording source.
-  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState());
+  layer->PushPropertiesTo(layer_impl, *host->GetPendingCommitState(),
+                          host->GetThreadUnsafeCommitState());
   UpdateDrawProperties(host_impl.pending_tree());
 
   host_impl.ActivateSyncTree();
diff --git a/cc/layers/scrollbar_layer_base.cc b/cc/layers/scrollbar_layer_base.cc
index e252082..148ce601 100644
--- a/cc/layers/scrollbar_layer_base.cc
+++ b/cc/layers/scrollbar_layer_base.cc
@@ -70,9 +70,11 @@
   SetNeedsCommit();
 }
 
-void ScrollbarLayerBase::PushPropertiesTo(LayerImpl* layer,
-                                          const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void ScrollbarLayerBase::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
 
   auto* scrollbar_layer_impl = static_cast<ScrollbarLayerImplBase*>(layer);
   DCHECK_EQ(scrollbar_layer_impl->orientation(), orientation_);
diff --git a/cc/layers/scrollbar_layer_base.h b/cc/layers/scrollbar_layer_base.h
index 86bd44a..eb5cd0b 100644
--- a/cc/layers/scrollbar_layer_base.h
+++ b/cc/layers/scrollbar_layer_base.h
@@ -25,7 +25,8 @@
   }
 
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   enum ScrollbarLayerType {
     kSolidColor,
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index f09f4ef..c50bd28 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -249,7 +249,8 @@
   // Simulate commit to compositor thread.
   scrollbar_layer->PushPropertiesTo(
       scrollbar_layer->CreateLayerImpl(layer_tree_host_->active_tree()).get(),
-      *layer_tree_host_->GetPendingCommitState());
+      *layer_tree_host_->GetPendingCommitState(),
+      layer_tree_host_->GetThreadUnsafeCommitState());
   scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(false);
   scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(false);
 
@@ -1336,7 +1337,8 @@
   // Simulate commit to compositor thread.
   scrollbar_layer->PushPropertiesTo(
       scrollbar_layer->CreateLayerImpl(layer_tree_host_->active_tree()).get(),
-      *layer_tree_host_->GetPendingCommitState());
+      *layer_tree_host_->GetPendingCommitState(),
+      layer_tree_host_->GetThreadUnsafeCommitState());
   scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(false);
   scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(false);
 
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index 521c309..e49a864 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -155,9 +155,11 @@
     layer_tree_host()->AddSurfaceRange(surface_range_);
 }
 
-void SurfaceLayer::PushPropertiesTo(LayerImpl* layer,
-                                    const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void SurfaceLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
   TRACE_EVENT0("cc", "SurfaceLayer::PushPropertiesTo");
   SurfaceLayerImpl* layer_impl = static_cast<SurfaceLayerImpl*>(layer);
   layer_impl->SetRange(surface_range_, std::move(deadline_in_frames_));
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h
index c19a755..7f8599b 100644
--- a/cc/layers/surface_layer.h
+++ b/cc/layers/surface_layer.h
@@ -65,7 +65,8 @@
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   const viz::SurfaceId& surface_id() const { return surface_range_.end(); }
 
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index 2e0ee94..b1ab993d 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -191,9 +191,11 @@
   return true;
 }
 
-void TextureLayer::PushPropertiesTo(LayerImpl* layer,
-                                    const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void TextureLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
   TRACE_EVENT0("cc", "TextureLayer::PushPropertiesTo");
 
   TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
index 8c715fb5..3501e76 100644
--- a/cc/layers/texture_layer.h
+++ b/cc/layers/texture_layer.h
@@ -156,7 +156,8 @@
   bool Update() override;
   bool IsSnappedToPixelGridInTarget() override;
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   // Request a mapping from SharedBitmapId to SharedMemory be registered via the
   // LayerTreeFrameSink with the display compositor. Once this mapping is
diff --git a/cc/layers/ui_resource_layer.cc b/cc/layers/ui_resource_layer.cc
index 8665b9c..7fda005 100644
--- a/cc/layers/ui_resource_layer.cc
+++ b/cc/layers/ui_resource_layer.cc
@@ -99,9 +99,11 @@
   return resource_id_ && Layer::HasDrawableContent();
 }
 
-void UIResourceLayer::PushPropertiesTo(LayerImpl* layer,
-                                       const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+void UIResourceLayer::PushPropertiesTo(
+    LayerImpl* layer,
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
   TRACE_EVENT0("cc", "UIResourceLayer::PushPropertiesTo");
   UIResourceLayerImpl* layer_impl = static_cast<UIResourceLayerImpl*>(layer);
 
diff --git a/cc/layers/ui_resource_layer.h b/cc/layers/ui_resource_layer.h
index bb25356..101a76f 100644
--- a/cc/layers/ui_resource_layer.h
+++ b/cc/layers/ui_resource_layer.h
@@ -24,7 +24,8 @@
   UIResourceLayer& operator=(const UIResourceLayer&) = delete;
 
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   void SetLayerTreeHost(LayerTreeHost* host) override;
 
diff --git a/cc/metrics/dropped_frame_counter.cc b/cc/metrics/dropped_frame_counter.cc
index db2a684..e6cb1c6f 100644
--- a/cc/metrics/dropped_frame_counter.cc
+++ b/cc/metrics/dropped_frame_counter.cc
@@ -103,7 +103,7 @@
 uint32_t DroppedFrameCounter::GetAverageThroughput() const {
   size_t good_frames = 0;
   for (auto it = --end(); it; --it) {
-    if (**it == kFrameStateComplete)
+    if (**it == kFrameStateComplete || **it == kFrameStatePartial)
       ++good_frames;
   }
   double throughput = 100. * good_frames / ring_buffer_.BufferSize();
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc
index a77fd17..fb1af918 100644
--- a/cc/test/fake_layer_tree_host.cc
+++ b/cc/test/fake_layer_tree_host.cc
@@ -69,11 +69,23 @@
 
 void FakeLayerTreeHost::SetNeedsCommit() { needs_commit_ = true; }
 
-std::unique_ptr<LayerTreeHostImpl> FakeLayerTreeHost::CreateLayerTreeHostImpl(
-    LayerTreeHostImplClient* client) {
+std::unique_ptr<LayerTreeHostImpl>
+FakeLayerTreeHost::CreateLayerTreeHostImplInternal(
+    LayerTreeHostImplClient* client,
+    MutatorHost*,
+    const LayerTreeSettings& settings,
+    TaskRunnerProvider* task_runner_provider,
+    raw_ptr<RasterDarkModeFilter>&,
+    int,
+    raw_ptr<TaskGraphRunner>& task_graph_runner,
+    scoped_refptr<base::SequencedTaskRunner>,
+    LayerTreeHostSchedulingClient*,
+    RenderingStatsInstrumentation*,
+    std::unique_ptr<UkmRecorderFactory>&,
+    base::WeakPtr<CompositorDelegateForInput>&) {
   DCHECK(!host_impl_);
   auto host_impl = std::make_unique<FakeLayerTreeHostImpl>(
-      GetSettings(), &task_runner_provider(), task_graph_runner());
+      settings, task_runner_provider, task_graph_runner);
   host_impl_ = host_impl.get();
   return host_impl;
 }
diff --git a/cc/test/fake_layer_tree_host.h b/cc/test/fake_layer_tree_host.h
index e8cedb33..2928dea 100644
--- a/cc/test/fake_layer_tree_host.h
+++ b/cc/test/fake_layer_tree_host.h
@@ -66,8 +66,20 @@
   void SetNeedsCommit() override;
   void SetNeedsUpdateLayers() override {}
 
-  std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImpl(
-      LayerTreeHostImplClient* client) override;
+  std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImplInternal(
+      LayerTreeHostImplClient* client,
+      MutatorHost* mutator_host,
+      const LayerTreeSettings& settings,
+      TaskRunnerProvider* task_runner_provider,
+      raw_ptr<RasterDarkModeFilter>& dark_mode_filter,
+      int id,
+      raw_ptr<TaskGraphRunner>& task_graph_runner,
+      scoped_refptr<base::SequencedTaskRunner> image_worker_task_runner,
+      LayerTreeHostSchedulingClient* scheduling_client,
+      RenderingStatsInstrumentation* rendering_stats_instrumentation,
+      std::unique_ptr<UkmRecorderFactory>& ukm_recorder_factory,
+      base::WeakPtr<CompositorDelegateForInput>& compositor_delegate_weak_ptr)
+      override;
 
   // This method is exposed for tests that don't use a Proxy (the
   // initialization of which would call the overridden CreateLayerTreeHostImpl
diff --git a/cc/test/fake_painted_scrollbar_layer.cc b/cc/test/fake_painted_scrollbar_layer.cc
index 02fba27..c517cfea 100644
--- a/cc/test/fake_painted_scrollbar_layer.cc
+++ b/cc/test/fake_painted_scrollbar_layer.cc
@@ -60,8 +60,9 @@
 
 void FakePaintedScrollbarLayer::PushPropertiesTo(
     LayerImpl* layer,
-    const CommitState& commit_state) {
-  PaintedScrollbarLayer::PushPropertiesTo(layer, commit_state);
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  PaintedScrollbarLayer::PushPropertiesTo(layer, commit_state, unsafe_state);
   ++push_properties_count_;
 }
 
diff --git a/cc/test/fake_painted_scrollbar_layer.h b/cc/test/fake_painted_scrollbar_layer.h
index bb7a08d..01bb7c6 100644
--- a/cc/test/fake_painted_scrollbar_layer.h
+++ b/cc/test/fake_painted_scrollbar_layer.h
@@ -34,7 +34,8 @@
   bool Update() override;
 
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
 
   using PaintedScrollbarLayer::IgnoreSetNeedsCommit;
 
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index c7e6d0e..59543a2 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -542,21 +542,33 @@
     return layer_tree_host;
   }
 
-  std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImpl(
-      LayerTreeHostImplClient* host_impl_client) override {
+  std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImplInternal(
+      LayerTreeHostImplClient* host_impl_client,
+      MutatorHost*,
+      const LayerTreeSettings& settings,
+      TaskRunnerProvider* task_runner_provider,
+      raw_ptr<RasterDarkModeFilter>&,
+      int,
+      raw_ptr<TaskGraphRunner>& task_graph_runner,
+      scoped_refptr<base::SequencedTaskRunner> image_worker_task_runner,
+      LayerTreeHostSchedulingClient* scheduling_client,
+      RenderingStatsInstrumentation* rendering_stats_instrumentation,
+      std::unique_ptr<UkmRecorderFactory>& ukm_recorder_factory,
+      base::WeakPtr<CompositorDelegateForInput>& compositor_delegate_weak_ptr)
+      override {
     std::unique_ptr<LayerTreeHostImpl> host_impl =
         LayerTreeHostImplForTesting::Create(
-            test_hooks_, GetSettings(), host_impl_client, scheduling_client(),
-            GetTaskRunnerProvider(), task_graph_runner(),
-            rendering_stats_instrumentation(), image_worker_task_runner_);
+            test_hooks_, settings, host_impl_client, scheduling_client,
+            task_runner_provider, task_graph_runner,
+            rendering_stats_instrumentation, image_worker_task_runner);
 
-    host_impl->InitializeUkm(ukm_recorder_factory_->CreateRecorder());
-    compositor_delegate_weak_ptr_ = host_impl->AsWeakPtr();
+    host_impl->InitializeUkm(ukm_recorder_factory->CreateRecorder());
+    compositor_delegate_weak_ptr = host_impl->AsWeakPtr();
 
     // Many tests using this class are specifically meant as input tests so
     // we'll need an input handler. Ideally these would be split out into a
     // separate test harness.
-    InputHandler::Create(*compositor_delegate_weak_ptr_);
+    InputHandler::Create(*compositor_delegate_weak_ptr);
 
     return host_impl;
   }
diff --git a/cc/test/push_properties_counting_layer.cc b/cc/test/push_properties_counting_layer.cc
index c2303a93..6bb9402 100644
--- a/cc/test/push_properties_counting_layer.cc
+++ b/cc/test/push_properties_counting_layer.cc
@@ -23,8 +23,9 @@
 
 void PushPropertiesCountingLayer::PushPropertiesTo(
     LayerImpl* layer,
-    const CommitState& commit_state) {
-  Layer::PushPropertiesTo(layer, commit_state);
+    const CommitState& commit_state,
+    const ThreadUnsafeCommitState& unsafe_state) {
+  Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
   AddPushPropertiesCount();
 }
 
diff --git a/cc/test/push_properties_counting_layer.h b/cc/test/push_properties_counting_layer.h
index 53c4a28c..e31b83d 100644
--- a/cc/test/push_properties_counting_layer.h
+++ b/cc/test/push_properties_counting_layer.h
@@ -25,7 +25,8 @@
 
   // Layer implementation.
   void PushPropertiesTo(LayerImpl* layer,
-                        const CommitState& commit_state) override;
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override;
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
 
   // Something to make this layer push properties, but no other layer.
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 0d37c85..8a286f5c 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -483,29 +483,54 @@
 
 std::unique_ptr<LayerTreeHostImpl> LayerTreeHost::CreateLayerTreeHostImpl(
     LayerTreeHostImplClient* client) {
+  // This method is special: it should be the only LayerTreeHost method that
+  // runs on the impl thread. As such, it cannot use LayerTreeHost getter
+  // methods that enforce DCHECK(IsMainThread()). Because it only ever runs when
+  // the main thread is blocked, it's safe to access member variables directly.
   DCHECK(task_runner_provider_->IsImplThread());
+  DCHECK(task_runner_provider_->IsMainThreadBlocked());
+  return CreateLayerTreeHostImplInternal(
+      client, thread_unsafe_commit_state_.mutator_host, settings_,
+      task_runner_provider_.get(), dark_mode_filter_, id_, task_graph_runner_,
+      image_worker_task_runner_, scheduling_client_,
+      rendering_stats_instrumentation_.get(), ukm_recorder_factory_,
+      compositor_delegate_weak_ptr_);
+}
 
+std::unique_ptr<LayerTreeHostImpl>
+LayerTreeHost::CreateLayerTreeHostImplInternal(
+    LayerTreeHostImplClient* client,
+    MutatorHost* mutator_host,
+    const LayerTreeSettings& settings,
+    TaskRunnerProvider* task_runner_provider,
+    raw_ptr<RasterDarkModeFilter>& dark_mode_filter,
+    int id,
+    raw_ptr<TaskGraphRunner>& task_graph_runner,
+    scoped_refptr<base::SequencedTaskRunner> image_worker_task_runner,
+    LayerTreeHostSchedulingClient* scheduling_client,
+    RenderingStatsInstrumentation* rendering_stats_instrumentation,
+    std::unique_ptr<UkmRecorderFactory>& ukm_recorder_factory,
+    base::WeakPtr<CompositorDelegateForInput>& compositor_delegate_weak_ptr) {
   std::unique_ptr<MutatorHost> mutator_host_impl =
-      mutator_host()->CreateImplInstance();
+      mutator_host->CreateImplInstance();
 
-  if (!settings_.scroll_animation_duration_for_testing.is_zero()) {
-    mutator_host()->SetScrollAnimationDurationForTesting(  // IN-TEST
-        settings_.scroll_animation_duration_for_testing);
+  if (!settings.scroll_animation_duration_for_testing.is_zero()) {
+    mutator_host->SetScrollAnimationDurationForTesting(  // IN-TEST
+        settings.scroll_animation_duration_for_testing);
   }
 
   std::unique_ptr<LayerTreeHostImpl> host_impl = LayerTreeHostImpl::Create(
-      settings_, client, task_runner_provider_.get(),
-      rendering_stats_instrumentation_.get(), task_graph_runner_,
-      std::move(mutator_host_impl), dark_mode_filter_, id_,
-      std::move(image_worker_task_runner_), scheduling_client_);
-  if (ukm_recorder_factory_) {
-    host_impl->InitializeUkm(ukm_recorder_factory_->CreateRecorder());
-    ukm_recorder_factory_.reset();
+      settings, client, task_runner_provider, rendering_stats_instrumentation,
+      task_graph_runner, std::move(mutator_host_impl), dark_mode_filter, id,
+      std::move(image_worker_task_runner), scheduling_client);
+  if (ukm_recorder_factory) {
+    host_impl->InitializeUkm(ukm_recorder_factory->CreateRecorder());
+    ukm_recorder_factory.reset();
   }
 
-  task_graph_runner_ = nullptr;
-  dark_mode_filter_ = nullptr;
-  compositor_delegate_weak_ptr_ = host_impl->AsWeakPtr();
+  task_graph_runner = nullptr;
+  dark_mode_filter = nullptr;
+  compositor_delegate_weak_ptr = host_impl->AsWeakPtr();
   return host_impl;
 }
 
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 65b4a5c..23c6b4a 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -683,7 +683,7 @@
   void RequestNewLayerTreeFrameSink();
   void DidInitializeLayerTreeFrameSink();
   void DidFailToInitializeLayerTreeFrameSink();
-  virtual std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImpl(
+  std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImpl(
       LayerTreeHostImplClient* client);
   void DidLoseLayerTreeFrameSink();
   void DidCommitAndDrawFrame() { client_->DidCommitAndDrawFrame(); }
@@ -853,6 +853,20 @@
   // free of slow-paths before toggling the flag.
   enum { kNumFramesToConsiderBeforeRemovingSlowPathFlag = 60 };
 
+  virtual std::unique_ptr<LayerTreeHostImpl> CreateLayerTreeHostImplInternal(
+      LayerTreeHostImplClient* client,
+      MutatorHost* mutator_host,
+      const LayerTreeSettings& settings,
+      TaskRunnerProvider* task_runner_provider,
+      raw_ptr<RasterDarkModeFilter>& dark_mode_filter,
+      int id,
+      raw_ptr<TaskGraphRunner>& task_graph_runner,
+      scoped_refptr<base::SequencedTaskRunner> image_worker_task_runner,
+      LayerTreeHostSchedulingClient* scheduling_client,
+      RenderingStatsInstrumentation* rendering_stats_instrumentation,
+      std::unique_ptr<UkmRecorderFactory>& ukm_recorder_factory,
+      base::WeakPtr<CompositorDelegateForInput>& compositor_delegate_weak_ptr);
+
   void ApplyViewportChanges(const CompositorCommitData& commit_data);
   void ApplyPageScaleDeltaFromImplSide(float page_scale_delta);
   void InitializeProxy(std::unique_ptr<Proxy> proxy);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 8d463393..dcdf461 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -1026,6 +1026,17 @@
 
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
+  void WillCommit(const CommitState&) override {
+    root_transform_index_ = root_->transform_tree_index();
+    child_transform_index_ = child_->transform_tree_index();
+    root_effect_index_ = root_->effect_tree_index();
+    child_effect_index_ = child_->effect_tree_index();
+    root_clip_index_ = root_->clip_tree_index();
+    child_clip_index_ = child_->clip_tree_index();
+    root_scroll_index_ = root_->scroll_tree_index();
+    child_scroll_index_ = child_->scroll_tree_index();
+  }
+
   void DidCommit() override {
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 1:
@@ -1052,21 +1063,21 @@
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
     PropertyTrees* property_trees = impl->sync_tree()->property_trees();
     const TransformNode* root_transform_node =
-        property_trees->transform_tree.Node(root_->transform_tree_index());
+        property_trees->transform_tree.Node(root_transform_index_);
     const TransformNode* child_transform_node =
-        property_trees->transform_tree.Node(child_->transform_tree_index());
+        property_trees->transform_tree.Node(child_transform_index_);
     const EffectNode* root_effect_node =
-        property_trees->effect_tree.Node(root_->effect_tree_index());
+        property_trees->effect_tree.Node(root_effect_index_);
     const EffectNode* child_effect_node =
-        property_trees->effect_tree.Node(child_->effect_tree_index());
+        property_trees->effect_tree.Node(child_effect_index_);
     const ClipNode* root_clip_node =
-        property_trees->clip_tree.Node(root_->clip_tree_index());
+        property_trees->clip_tree.Node(root_clip_index_);
     const ClipNode* child_clip_node =
-        property_trees->clip_tree.Node(child_->clip_tree_index());
+        property_trees->clip_tree.Node(child_clip_index_);
     const ScrollNode* root_scroll_node =
-        property_trees->scroll_tree.Node(root_->scroll_tree_index());
+        property_trees->scroll_tree.Node(root_scroll_index_);
     const ScrollNode* child_scroll_node =
-        property_trees->scroll_tree.Node(child_->scroll_tree_index());
+        property_trees->scroll_tree.Node(child_scroll_index_);
     switch (impl->sync_tree()->source_frame_number()) {
       case 0:
         // root_ should create transform, scroll and effect tree nodes but not
@@ -1112,6 +1123,14 @@
  private:
   scoped_refptr<Layer> root_;
   scoped_refptr<Layer> child_;
+  int root_transform_index_ = TransformTree::kInvalidNodeId;
+  int child_transform_index_ = TransformTree::kInvalidNodeId;
+  int root_effect_index_ = EffectTree::kInvalidNodeId;
+  int child_effect_index_ = EffectTree::kInvalidNodeId;
+  int root_clip_index_ = ClipTree::kInvalidNodeId;
+  int child_clip_index_ = ClipTree::kInvalidNodeId;
+  int root_scroll_index_ = ScrollTree::kInvalidNodeId;
+  int child_scroll_index_ = ScrollTree::kInvalidNodeId;
 };
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestPushNodeOwnerToNodeIdMap);
@@ -2040,9 +2059,14 @@
 
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
+  void WillCommit(const CommitState&) override {
+    root_effect_tree_index_ =
+        layer_tree_host()->root_layer()->effect_tree_index();
+  }
+
   void DidCommit() override {
     EffectTree& effect_tree = layer_tree_host()->property_trees()->effect_tree;
-    EffectNode* node = effect_tree.Node(root_->effect_tree_index());
+    EffectNode* node = effect_tree.Node(root_effect_tree_index_);
     switch (layer_tree_host()->SourceFrameNumber()) {
       case 1:
         node->opacity = 0.5f;
@@ -2078,7 +2102,7 @@
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
     EffectTree& effect_tree = impl->sync_tree()->property_trees()->effect_tree;
     LayerImpl* root = impl->sync_tree()->root_layer();
-    EffectNode* node = effect_tree.Node(root->effect_tree_index());
+    EffectNode* node = effect_tree.Node(root_effect_tree_index_);
     switch (impl->sync_tree()->source_frame_number()) {
       case 0:
         impl->sync_tree()->SetOpacityMutated(root->element_id(), 0.75f);
@@ -2126,6 +2150,7 @@
 
  private:
   scoped_refptr<Layer> root_;
+  int root_effect_tree_index_ = EffectTree::kInvalidNodeId;
   FilterOperations blur_filter_;
   FilterOperations brightness_filter_;
   FilterOperations sepia_filter_;
@@ -2149,10 +2174,14 @@
 
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
+  void WillCommit(const CommitState&) override {
+    transform_tree_index_ = layer_->transform_tree_index();
+  }
+
   void DidCommit() override {
     TransformTree& transform_tree =
         layer_tree_host()->property_trees()->transform_tree;
-    TransformNode* node = transform_tree.Node(layer_->transform_tree_index());
+    TransformNode* node = transform_tree.Node(transform_tree_index_);
     gfx::Transform rotate10;
     rotate10.Rotate(10.f);
     switch (layer_tree_host()->SourceFrameNumber()) {
@@ -2209,6 +2238,7 @@
 
  private:
   scoped_refptr<Layer> layer_;
+  int transform_tree_index_ = TransformTree::kInvalidNodeId;
 };
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestTransformTreeSync);
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 9e9a0fa..65b4720 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -1484,6 +1484,7 @@
     CreateTransformNode(scroller_.get());
     CreateScrollNode(scroller_.get(),
                      layer_tree_host()->root_layer()->bounds());
+    scroll_tree_index_ = scroller_->scroll_tree_index();
     layer_tree_host()->root_layer()->AddChild(scroller_.get());
   }
 
@@ -1496,7 +1497,7 @@
     ++cur_step_;
 
     ScrollTree& scroll_tree = layer_tree_host()->property_trees()->scroll_tree;
-    ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
+    ScrollNode* scroll_node = scroll_tree.Node(scroll_tree_index_);
     switch (cur_step_) {
       case 1:
         // Set max_scroll_offset = (100, 100).
@@ -1522,7 +1523,7 @@
 
     ScrollTree& scroll_tree =
         impl->active_tree()->property_trees()->scroll_tree;
-    ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
+    ScrollNode* scroll_node = scroll_tree.Node(scroll_tree_index_);
 
     ScrollStateData scroll_state_data;
     scroll_state_data.is_beginning = true;
@@ -1572,6 +1573,7 @@
 
  private:
   int cur_step_ = 0;
+  int scroll_tree_index_ = ScrollTree::kInvalidNodeId;
   scoped_refptr<Layer> scroller_;
 };
 
@@ -1652,14 +1654,14 @@
     CreateTransformNode(scroller_.get());
     CreateScrollNode(scroller_.get(),
                      layer_tree_host()->root_layer()->bounds());
+    scroll_tree_index_ = scroller_->scroll_tree_index();
     layer_tree_host()->root_layer()->AddChild(scroller_.get());
   }
 
   void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
     ScrollTree& scroll_tree =
         impl->active_tree()->property_trees()->scroll_tree;
-    ScrollNode* scroller_scroll_node =
-        scroll_tree.Node(scroller_->scroll_tree_index());
+    ScrollNode* scroller_scroll_node = scroll_tree.Node(scroll_tree_index_);
 
     ScrollStateData scroll_state_data;
     scroll_state_data.is_beginning = true;
@@ -1720,6 +1722,7 @@
 
  private:
   scoped_refptr<Layer> scroller_;
+  int scroll_tree_index_ = ScrollTree::kInvalidNodeId;
 };
 
 // This test is flaky in the single threaded configuration, only on the
@@ -2679,13 +2682,18 @@
 
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
 
+  void WillCommit(const CommitState&) override {
+    middle_scrollable_scroll_tree_index_ =
+        middle_scrollable_->scroll_tree_index();
+  }
+
   void CommitCompleteOnThread(LayerTreeHostImpl* impl) override {
     if (TestEnded())
       return;
 
     ScrollNode* scroll_node =
         impl->active_tree()->property_trees()->scroll_tree.Node(
-            middle_scrollable_->scroll_tree_index());
+            middle_scrollable_scroll_tree_index_);
 
     // The top-left hit should immediately hit the top layer's non-fast region.
     {
@@ -2756,6 +2764,7 @@
   scoped_refptr<Layer> bottom_;
   scoped_refptr<Layer> middle_scrollable_;
   scoped_refptr<Layer> top_;
+  int middle_scrollable_scroll_tree_index_ = ScrollTree::kInvalidNodeId;
 };
 
 SINGLE_THREAD_TEST_F(NonScrollingNonFastScrollableRegion);
@@ -2785,6 +2794,10 @@
     layer_tree_host()->root_layer()->AddChild(layer_);
   }
 
+  void WillCommit(const CommitState&) override {
+    scroll_tree_index_ = layer_->scroll_tree_index();
+  }
+
   void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
     if (is_done_)
       return;
@@ -2802,8 +2815,7 @@
           ui::ScrollInputType::kTouchscreen);
 
       ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
-      ASSERT_EQ(layer_->scroll_tree_index(),
-                impl->CurrentlyScrollingNode()->id);
+      ASSERT_EQ(scroll_tree_index_, impl->CurrentlyScrollingNode()->id);
 
       impl->GetInputHandler().ScrollUpdate(
           UpdateState(gfx::Point(), gfx::Vector2dF(0, 10)).get());
@@ -2819,6 +2831,7 @@
  private:
   bool is_done_ = false;
   scoped_refptr<Layer> layer_;
+  int scroll_tree_index_ = ScrollTree::kInvalidNodeId;
   FakeContentLayerClient client_;
   base::test::ScopedFeatureList scoped_feature_list;
 };
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 701176c0..d697b93 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -78,7 +78,10 @@
   DCHECK(settings.single_thread_proxy_scheduler ||
          !settings.enable_checker_imaging)
       << "Checker-imaging is not supported in synchronous single threaded mode";
-  host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this);
+  {
+    DebugScopedSetMainThreadBlocked main_thread_blocked(task_runner_provider_);
+    host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this);
+  }
   if (settings.single_thread_proxy_scheduler && !scheduler_on_impl_thread_) {
     SchedulerSettings scheduler_settings(settings.ToSchedulerSettings());
     scheduler_settings.commit_to_active_tree = true;
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index 21fbe7e4..dac8c82 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -26,8 +26,18 @@
 namespace cc {
 namespace {
 #if DCHECK_IS_ON()
-template <typename LayerType>
-static void AssertValidPropertyTreeIndices(LayerType* layer) {
+static void AssertValidPropertyTreeIndices(
+    const Layer* layer,
+    const PropertyTrees& property_trees) {
+  DCHECK(layer);
+  DCHECK(layer->transform_tree_index_is_valid(property_trees));
+  DCHECK(layer->effect_tree_index_is_valid(property_trees));
+  DCHECK(layer->clip_tree_index_is_valid(property_trees));
+  DCHECK(layer->scroll_tree_index_is_valid(property_trees));
+}
+
+static void AssertValidPropertyTreeIndices(const LayerImpl* layer,
+                                           const PropertyTrees&) {
   DCHECK(layer);
   DCHECK_NE(layer->transform_tree_index(), TransformTree::kInvalidNodeId);
   DCHECK_NE(layer->effect_tree_index(), EffectTree::kInvalidNodeId);
@@ -35,7 +45,7 @@
   DCHECK_NE(layer->scroll_tree_index(), ScrollTree::kInvalidNodeId);
 }
 
-static bool LayerHasValidPropertyTreeIndices(LayerImpl* layer) {
+static bool LayerHasValidPropertyTreeIndices(const LayerImpl* layer) {
   DCHECK(layer);
   return layer->transform_tree_index() != TransformTree::kInvalidNodeId &&
          layer->effect_tree_index() != EffectTree::kInvalidNodeId &&
@@ -72,7 +82,8 @@
 template <typename LayerTreeType>
 void PushLayerList(OwnedLayerImplMap* old_layers,
                    LayerTreeType* host,
-                   LayerTreeImpl* tree_impl) {
+                   LayerTreeImpl* tree_impl,
+                   const PropertyTrees& property_trees) {
   DCHECK(tree_impl->LayerListIsEmpty());
   for (auto* layer : *host) {
     std::unique_ptr<LayerImpl> layer_impl(
@@ -80,7 +91,7 @@
 
 #if DCHECK_IS_ON()
     // Every layer should have valid property tree indices
-    AssertValidPropertyTreeIndices(layer);
+    AssertValidPropertyTreeIndices(layer, property_trees);
     // Every layer_impl should either have valid property tree indices already
     // or the corresponding layer should push them onto layer_impl.
     DCHECK(LayerHasValidPropertyTreeIndices(layer_impl.get()) ||
@@ -94,7 +105,8 @@
 
 template <typename LayerTreeType>
 void SynchronizeTreesInternal(LayerTreeType* source_tree,
-                              LayerTreeImpl* tree_impl) {
+                              LayerTreeImpl* tree_impl,
+                              const PropertyTrees& property_trees) {
   DCHECK(tree_impl);
 
   TRACE_EVENT0("cc", "TreeSynchronizer::SynchronizeTrees");
@@ -106,7 +118,7 @@
     old_layer_map[it->id()] = std::move(it);
   }
 
-  PushLayerList(&old_layer_map, source_tree, tree_impl);
+  PushLayerList(&old_layer_map, source_tree, tree_impl, property_trees);
 }
 
 }  // namespace
@@ -117,7 +129,8 @@
   if (!unsafe_state.root_layer) {
     tree_impl->DetachLayers();
   } else {
-    SynchronizeTreesInternal(&unsafe_state, tree_impl);
+    SynchronizeTreesInternal(&unsafe_state, tree_impl,
+                             unsafe_state.property_trees);
   }
 }
 
@@ -126,7 +139,8 @@
   if (pending_tree->LayerListIsEmpty()) {
     active_tree->DetachLayers();
   } else {
-    SynchronizeTreesInternal(pending_tree, active_tree);
+    SynchronizeTreesInternal(pending_tree, active_tree,
+                             *pending_tree->property_trees());
   }
 }
 
@@ -170,7 +184,7 @@
     auto* source_layer = *it;
     LayerImpl* target_layer = impl_tree->LayerById(source_layer->id());
     DCHECK(target_layer);
-    source_layer->PushPropertiesTo(target_layer, commit_state);
+    source_layer->PushPropertiesTo(target_layer, commit_state, unsafe_state);
   }
   unsafe_state.layers_that_should_push_properties.clear();
 }
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index a0a3e5af..922faec1 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -80,8 +80,9 @@
   }
 
   void PushPropertiesTo(LayerImpl* layer_impl,
-                        const CommitState& commit_state) override {
-    Layer::PushPropertiesTo(layer_impl, commit_state);
+                        const CommitState& commit_state,
+                        const ThreadUnsafeCommitState& unsafe_state) override {
+    Layer::PushPropertiesTo(layer_impl, commit_state, unsafe_state);
 
     MockLayerImpl* mock_layer_impl = static_cast<MockLayerImpl*>(layer_impl);
     mock_layer_impl->SetLayerImplDestructionList(layer_impl_destruction_list_);
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index f25c7aa5..29972fe 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -5,6 +5,7 @@
 import("//android_webview/variables.gni")
 import("//base/android/linker/config.gni")
 import("//base/android/resource_exclusions.gni")
+import("//build/config/android/create_unwind_table.gni")
 import("//build/config/android/extract_unwind_tables.gni")
 import("//build/config/android/rules.gni")
 import("//build/config/compiler/compiler.gni")
@@ -100,25 +101,46 @@
 
   if (_add_unwind_tables) {
     _unwind_asset_target = "${target_name}__unwind_assets"
-    unwind_table_asset(_unwind_asset_target) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
 
-      if (defined(invoker.shared_library_for_unwind_asset)) {
-        library_target = invoker.shared_library_for_unwind_asset
-      } else {
-        if (_is_monochrome || _is_trichrome) {
+    if (use_android_unwinder_v2) {
+      unwind_table_asset_v2(_unwind_asset_target) {
+        if (defined(invoker.testonly)) {
+          testonly = invoker.testonly
+        }
+
+        if (defined(invoker.shared_library_for_unwind_asset)) {
+          library_target = invoker.shared_library_for_unwind_asset
+        } else if (_is_monochrome || _is_trichrome) {
           library_target = "monochrome"
         } else {
           library_target = "chrome"
         }
-      }
 
-      if (android_64bit_target_cpu) {
-        deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
-      } else {
-        deps = [ "//chrome/android:lib${library_target}" ]
+        if (android_64bit_target_cpu) {
+          deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
+        } else {
+          deps = [ "//chrome/android:lib${library_target}" ]
+        }
+      }
+    } else {
+      unwind_table_asset(_unwind_asset_target) {
+        if (defined(invoker.testonly)) {
+          testonly = invoker.testonly
+        }
+
+        if (defined(invoker.shared_library_for_unwind_asset)) {
+          library_target = invoker.shared_library_for_unwind_asset
+        } else if (_is_monochrome || _is_trichrome) {
+          library_target = "monochrome"
+        } else {
+          library_target = "chrome"
+        }
+
+        if (android_64bit_target_cpu) {
+          deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
+        } else {
+          deps = [ "//chrome/android:lib${library_target}" ]
+        }
       }
     }
   } else if (defined(invoker.shared_library_for_unwind_asset)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProvider.java
index 8277c431..fdd0d1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/quickactionsearchwidget/QuickActionSearchWidgetProvider.java
@@ -149,7 +149,7 @@
         Intent dinoIntent = createDinoIntent(context);
 
         sDelegate = new QuickActionSearchWidgetProviderDelegate(
-                searchActivityComponent, trustedIncognitoIntent, dinoIntent);
+                context, searchActivityComponent, trustedIncognitoIntent, dinoIntent);
         return sDelegate;
     }
 
diff --git a/chrome/app/OWNERS b/chrome/app/OWNERS
index c2830d1..06808ac 100644
--- a/chrome/app/OWNERS
+++ b/chrome/app/OWNERS
@@ -16,6 +16,8 @@
 per-file os_settings_strings.grdp=*
 per-file os_settings_search_tag_strings.grdp=file://chrome/browser/resources/settings/chromeos/OWNERS
 
+per-file access_code_cast_strings.grdp=file://chrome/browser/ui/webui/access_code_cast/OWNERS
+
 per-file app_management_strings.grdp=file://chrome/browser/ui/webui/app_management/OWNERS
 
 per-file web_time_limit_error_page_strings.grdp=file://chrome/browser/ash/child_accounts/OWNERS
diff --git a/chrome/app/access_code_cast_strings.grdp b/chrome/app/access_code_cast_strings.grdp
new file mode 100644
index 0000000..5fc1301
--- /dev/null
+++ b/chrome/app/access_code_cast_strings.grdp
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Access Code Cast-specific strings (included from generated_resources.grd). -->
+<grit-part>
+  <message name="IDS_ACCESS_CODE_CAST_BACK" desc="Label for the 'back' button to return to the previous screen">
+    Back
+  </message>
+  <message name="IDS_ACCESS_CODE_CAST_CAST" desc="Label for the button to start casting">
+    Cast
+  </message>
+  <message name="IDS_ACCESS_CODE_CAST_DIALOG_TITLE" desc="Title for access code cast dialog">
+    Cast to a new display
+  </message>
+  <message name="IDS_ACCESS_CODE_CAST_USE_CAMERA" desc="Label for the button to use the device camera to scan a QR code">
+    Use the camera to scan QR code
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_BACK.png.sha1 b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_BACK.png.sha1
new file mode 100644
index 0000000..dcb1c6ca
--- /dev/null
+++ b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_BACK.png.sha1
@@ -0,0 +1 @@
+91687f84bc741017567a8e2ac9f6678066b0b877
\ No newline at end of file
diff --git a/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_CAST.png.sha1 b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_CAST.png.sha1
new file mode 100644
index 0000000..b49ce48
--- /dev/null
+++ b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_CAST.png.sha1
@@ -0,0 +1 @@
+cb57fa67c57448a6862dcac40af6059e8a8a20e9
\ No newline at end of file
diff --git a/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_DIALOG_TITLE.png.sha1 b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..b49ce48
--- /dev/null
+++ b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+cb57fa67c57448a6862dcac40af6059e8a8a20e9
\ No newline at end of file
diff --git a/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_USE_CAMERA.png.sha1 b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_USE_CAMERA.png.sha1
new file mode 100644
index 0000000..b49ce48
--- /dev/null
+++ b/chrome/app/access_code_cast_strings_grdp/IDS_ACCESS_CODE_CAST_USE_CAMERA.png.sha1
@@ -0,0 +1 @@
+cb57fa67c57448a6862dcac40af6059e8a8a20e9
\ No newline at end of file
diff --git a/chrome/app/access_code_cast_strings_grdp/OWNERS b/chrome/app/access_code_cast_strings_grdp/OWNERS
new file mode 100644
index 0000000..ccb161d
--- /dev/null
+++ b/chrome/app/access_code_cast_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/webui/access_code_cast/OWNERS
\ No newline at end of file
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 22285b8..0de03b4 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -826,7 +826,7 @@
         </message>
       </if>
 
-      <if expr="not chromeos and not is_android">
+      <if expr="not chromeos and not is_android and not lacros">
         <!-- Dice Web Signin Interception Bubble-->
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble">
           Continue in a new Chromium profile?
@@ -846,7 +846,9 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_DEVICE" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed on a managed device">
           This will create a new Chromium profile for <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph>
         </message>
+      </if>
 
+      <if expr="not chromeos and not is_android">
         <!-- Profile Customization Bubble-->
         <message name="IDS_PROFILE_CUSTOMIZATION_TEXT" desc="Text of the profile customization bubble">
           Customize your new Chromium profile
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index dcac1852..e805481 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -277,6 +277,9 @@
   </translations>
   <release seq="1">
     <messages fallback_to_english="true">
+      <!-- Access Code Cast specific strings -->
+      <part file="access_code_cast_strings.grdp" />
+
       <!-- Bookmarks specific strings -->
       <part file="bookmarks_strings.grdp" />
 
@@ -5165,6 +5168,30 @@
       <message name="IDS_COMPONENTS_CHECKING_LABEL" desc="Checking label">
         Checking for status...
       </message>
+
+      <if expr="chromeos">
+        <message name="IDS_COMPONENTS_OS_TEXT1_LABEL" desc="Begin label text on the os://components page from ChromeOS which directs a user to a dialog showing Chrome browser specific components - enclosing url link.">
+          Looking for browser components? Visit
+        </message>
+        <message name="IDS_COMPONENTS_OS_TEXT2_LABEL" desc="End label text on the os://components page from ChromeOS which directs a user to a dialog showing Chrome browser specific components - enclosing url link.">
+          .
+        </message>
+        <message name="IDS_COMPONENTS_OS_LINK" translateable="false" desc="Label for a button on the os://components page from ChromeOS that will call a new window which allows to change Chrome browser components.">
+          chrome://components
+        </message>
+      </if>
+      <if expr="lacros">
+        <message name="IDS_COMPONENTS_OS_TEXT1_LABEL" desc="Begin label text on the chrome://components page from ChromeOS which directs a user to a dialog showing OS specific components - enclosing url link.">
+          Looking for system components? Visit
+        </message>
+        <message name="IDS_COMPONENTS_OS_TEXT2_LABEL" desc="End label text on the chrome://components page from ChromeOS which directs a user to a dialog showing OS specific components - enclosing url link.">
+          .
+        </message>
+        <message name="IDS_COMPONENTS_OS_LINK" translateable="false" desc="Label for button on the chrome://components page from ChromeOS that will call a new window which allows to change system components.">
+          os://components
+        </message>
+      </if>
+
       <message name="IDS_COMPONENTS_SVC_STATUS_NEW" desc="Service Status">
         New
       </message>
@@ -5557,6 +5584,29 @@
         </message>
       </if>
 
+      <if expr="chromeos">
+        <message name="IDS_ABOUT_OS_TEXT1_LABEL" desc="Begin label text on the os://about page from ChromeOS which directs a user to a dialog showing Chrome browser specific about page - enclosing url link.">
+          Looking for the browser about page? Visit
+        </message>
+        <message name="IDS_ABOUT_OS_TEXT2_LABEL" desc="End label text on the os://about page from ChromeOS which directs a user to a dialog showing Chrome browser specific about page - enclosing url link.">
+          .
+        </message>
+        <message name="IDS_ABOUT_OS_LINK" translateable="false" desc="Label for button on the os://about page from ChromeOS which shows the user the link to the Chrome browser specific about page.">
+          chrome://about
+        </message>
+      </if>
+      <if expr="lacros">
+        <message name="IDS_ABOUT_OS_TEXT1_LABEL" desc="Begin label text on the chrome://about page from ChromeOS which directs a user to a dialog showing system specific about page - enclosing url link.">
+          Looking for the system about page? Visit
+        </message>
+        <message name="IDS_ABOUT_OS_TEXT2_LABEL" desc="End label text on the chrome://about page from ChromeOS which directs a user to a dialog showing system specific about page - enclosing url link.">
+          .
+        </message>
+        <message name="IDS_ABOUT_OS_LINK" translateable="false" desc="Label for button on the chrome://about page from ChromeOS which shows the user the link to the system specific about page.">
+          os://about
+        </message>
+      </if>
+
       <!-- NaCl plugin strings -->
       <message name="IDS_NACL_APP_MISSING_ARCH_MESSAGE" desc="Shown if a Native Client app does not support the current architecture">
         This page uses a Native Client app that doesn't work on your computer.
@@ -8751,7 +8801,7 @@
       </message>
 
       <!-- Dice Web Signin Interception Bubble-->
-      <if expr="not chromeos and not is_android">
+      <if expr="not chromeos and not is_android and not lacros">
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_NEW_PROFILE_BUTTON_LABEL" desc="Label of the confirmation button in the web signin interception bubble">
           Ok
         </message>
@@ -8776,14 +8826,17 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed">
           This will separate your browsing from <ph name="EXISTING_USER">$1<ex>bob@example.com</ex></ph>
         </message>
-        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME" desc="Default name for work profiles created after signin interception">
-          Work
-        </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2" desc="Description for the profile switch interception bubble">
           <ph name="NAME">$2<ex>Bob</ex></ph>'s profile is linked to <ph name="EMAIL">$1<ex>bob@gmail.com</ex></ph>
         </message>
       </if>
 
+      <if expr="not chromeos and not is_android">
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_PROFILE_NAME" desc="Default name for work profiles created after signin interception">
+          Work
+        </message>
+      </if>
+
       <!-- Signin Error tab modal dialog -->
       <message name="IDS_SIGNIN_ERROR_TITLE" desc="Title of the signin error tab modal dialog.">
         Can't sign in
diff --git a/chrome/app/generated_resources_grd/IDS_ABOUT_OS_LINK.png.sha1 b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_LINK.png.sha1
new file mode 100644
index 0000000..c50fed8f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_LINK.png.sha1
@@ -0,0 +1 @@
+7f3f1b982f4fac9ea5cfeb4763011c6e4de0cba7
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT1_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT1_LABEL.png.sha1
new file mode 100644
index 0000000..c50fed8f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT1_LABEL.png.sha1
@@ -0,0 +1 @@
+7f3f1b982f4fac9ea5cfeb4763011c6e4de0cba7
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT2_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT2_LABEL.png.sha1
new file mode 100644
index 0000000..c50fed8f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ABOUT_OS_TEXT2_LABEL.png.sha1
@@ -0,0 +1 @@
+7f3f1b982f4fac9ea5cfeb4763011c6e4de0cba7
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_LINK.png.sha1 b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_LINK.png.sha1
new file mode 100644
index 0000000..c3d3ec9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_LINK.png.sha1
@@ -0,0 +1 @@
+ec210435910d664c6017f1c4eafc8d8a4fdd3347
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT1_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT1_LABEL.png.sha1
new file mode 100644
index 0000000..c3d3ec9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT1_LABEL.png.sha1
@@ -0,0 +1 @@
+ec210435910d664c6017f1c4eafc8d8a4fdd3347
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT2_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT2_LABEL.png.sha1
new file mode 100644
index 0000000..c3d3ec9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_COMPONENTS_OS_TEXT2_LABEL.png.sha1
@@ -0,0 +1 @@
+ec210435910d664c6017f1c4eafc8d8a4fdd3347
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index c6de1f5..cbe96ae 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -834,7 +834,7 @@
         </message>
       </if>
 
-      <if expr="not chromeos and not is_android">
+      <if expr="not chromeos and not is_android and not lacros">
         <!-- Dice Web Signin Interception Bubble-->
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CREATE_BUBBLE_TITLE" desc="Title of the web signin interception bubble">
           Continue in a new Chrome profile?
@@ -854,7 +854,9 @@
          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_DEVICE" desc="Body of the web signin interception bubble when the new account is personal and the existing account is managed on a managed device">
           This will create a new Chrome profile for <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph>
         </message>
+      </if>
 
+      <if expr="not chromeos and not is_android">
         <!-- Profile Customization Bubble-->
         <message name="IDS_PROFILE_CUSTOMIZATION_TEXT" desc="Text of the profile customization bubble">
           Customize your new Chrome profile
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 66d7e181..ef43db0 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -6372,15 +6372,6 @@
         "signin/signin_util_win.h",
       ]
     }
-
-    # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is no
-    # longer defined on Lacros.
-    if (is_chromeos_lacros) {
-      sources -= [
-        "signin/signin_manager_factory.cc",
-        "signin/signin_manager_factory.h",
-      ]
-    }
   }
 
   if (enable_media_remoting) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ce0b1ab8..432a5d1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6396,13 +6396,6 @@
      flag_descriptions::kReduceUserAgentDescription, kOsDesktop | kOsAndroid,
      FEATURE_VALUE_TYPE(blink::features::kReduceUserAgent)},
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-    {"enable-user-data-snapshot", flag_descriptions::kUserDataSnapshotName,
-     flag_descriptions::kUserDataSnapshotDescription,
-     kOsMac | kOsWin | kOsLinux | kOsFuchsia,
-     FEATURE_VALUE_TYPE(features::kUserDataSnapshot)},
-#endif
-
 #if defined(OS_WIN)
     {"run-video-capture-service-in-browser",
      flag_descriptions::kRunVideoCaptureServiceInBrowserProcessName,
diff --git a/chrome/browser/android/foreign_session_helper.cc b/chrome/browser/android/foreign_session_helper.cc
index 0db9c5c..1333e57 100644
--- a/chrome/browser/android/foreign_session_helper.cc
+++ b/chrome/browser/android/foreign_session_helper.cc
@@ -217,7 +217,7 @@
   base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
   std::unique_ptr<base::DictionaryValue> collapsed_sessions(
       pref_collapsed_sessions->DeepCopy());
-  pref_collapsed_sessions->Clear();
+  pref_collapsed_sessions->DictClear();
 
   ScopedJavaLocalRef<jobject> last_pushed_session;
 
diff --git a/chrome/browser/apps/app_service/intent_util_unittest.cc b/chrome/browser/apps/app_service/intent_util_unittest.cc
index 0a757b7..9e4057c 100644
--- a/chrome/browser/apps/app_service/intent_util_unittest.cc
+++ b/chrome/browser/apps/app_service/intent_util_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/values.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index 570d462d4..a3aa8a58 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -792,10 +792,10 @@
 
   DictionaryPrefUpdate running_duration_update(profile_->GetPrefs(),
                                                kAppRunningDuration);
-  running_duration_update->Clear();
+  running_duration_update->DictClear();
   DictionaryPrefUpdate activated_count_update(profile_->GetPrefs(),
                                               kAppActivatedCount);
-  activated_count_update->Clear();
+  activated_count_update->DictClear();
 }
 
 void AppPlatformMetrics::RecordAppsCount(apps::mojom::AppType app_type) {
diff --git a/chrome/browser/apps/app_service/publisher_host.h b/chrome/browser/apps/app_service/publisher_host.h
index db02f50..f93b27e 100644
--- a/chrome/browser/apps/app_service/publisher_host.h
+++ b/chrome/browser/apps/app_service/publisher_host.h
@@ -10,6 +10,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
 
 namespace web_app {
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index b9d9cfa9..ff58070 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -7,6 +7,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/app_service_test.h"
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 3fd6e20..2285838 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -4609,10 +4609,7 @@
   }
 };
 
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || defined(OS_MAC) || \
-    defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_WIN)
 // This verifies the fix for http://crbug.com/667708.
 IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest,
                        ClosingChromeSignInShouldNotCrash) {
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
index a959522..92c47ee 100644
--- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/debug/dump_without_crashing.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
diff --git a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h
index 69ea7d0..9c0db17 100644
--- a/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h
+++ b/chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_APPS_INTENT_HELPER_METRICS_INTENT_HANDLING_METRICS_H_
 #define CHROME_BROWSER_APPS_INTENT_HELPER_METRICS_INTENT_HANDLING_METRICS_H_
 
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_types.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 0bc7475..7dcc6b02 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -1294,7 +1294,7 @@
                        MAYBE_IncognitoComponentApp) {
   // Get the file manager app.
   const Extension* file_manager = extension_registry()->GetExtensionById(
-      "hhaomjibdihmijegdhdafkllkbggdgoj", ExtensionRegistry::ENABLED);
+      extension_misc::kFilesManagerAppId, ExtensionRegistry::ENABLED);
   ASSERT_TRUE(file_manager != NULL);
   Profile* incognito_profile =
       profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_limits_allowlist_policy_test_utils.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_limits_allowlist_policy_test_utils.cc
index 764c322..e1f9539 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_time_limits_allowlist_policy_test_utils.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_time_limits_allowlist_policy_test_utils.cc
@@ -25,7 +25,7 @@
 void AppTimeLimitsAllowlistPolicyBuilder::Clear() {
   base::DictionaryValue* dict_value;
   value_.GetAsDictionary(&dict_value);
-  dict_value->Clear();
+  dict_value->DictClear();
 }
 
 void AppTimeLimitsAllowlistPolicyBuilder::AppendToAllowlistUrlList(
diff --git a/chrome/browser/ash/customization/customization_document.cc b/chrome/browser/ash/customization/customization_document.cc
index 70976a39..51b6dd4 100644
--- a/chrome/browser/ash/customization/customization_document.cc
+++ b/chrome/browser/ash/customization/customization_document.cc
@@ -742,14 +742,14 @@
             base::Value::AsDictionaryValue(app_entry_value);
         if (!app_entry.GetString(kIdAttr, &app_id)) {
           LOG(ERROR) << "Wrong format of default application list";
-          prefs->Clear();
+          prefs->DictClear();
           break;
         }
         entry = app_entry.CreateDeepCopy();
         entry->RemoveKey(kIdAttr);
       } else {
         LOG(ERROR) << "Wrong format of default application list";
-        prefs->Clear();
+        prefs->DictClear();
         break;
       }
       if (!entry->FindKey(
diff --git a/chrome/browser/ash/file_manager/path_util.h b/chrome/browser/ash/file_manager/path_util.h
index b318be77..742ad57d 100644
--- a/chrome/browser/ash/file_manager/path_util.h
+++ b/chrome/browser/ash/file_manager/path_util.h
@@ -34,7 +34,8 @@
 // Name of the mount point used to store temporary files for sharing.
 extern const char kShareCacheMountPointName[];
 
-// Returns FilesApp origin chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj.
+// Returns the valid FilesApp origin. It may be either the System Web App
+// chrome:// URL or the legacy Chrome App chrome-extension:// URL.
 const url::Origin& GetFilesAppOrigin();
 
 // Gets the absolute path for the 'Downloads' folder for the |profile|.
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
index a15705be..5dd577f1 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_manager_unittest.cc
@@ -226,7 +226,7 @@
   void ClearEventsDict() {
     base::DictionaryValue* mutable_dict;
     if (events_value_.GetAsDictionary(&mutable_dict))
-      mutable_dict->Clear();
+      mutable_dict->DictClear();
     else
       NOTREACHED();
   }
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
index fbbcb46..307484d 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_uploader_unittest.cc
@@ -127,7 +127,7 @@
   void ClearReportDict() {
     base::DictionaryValue* mutable_dict;
     if (value_report_.GetAsDictionary(&mutable_dict))
-      mutable_dict->Clear();
+      mutable_dict->DictClear();
     else
       NOTREACHED();
   }
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_unittest.cc b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_unittest.cc
index 82fb408..48705db6 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_log_manager_unittest.cc
@@ -219,7 +219,7 @@
   void ClearEventsDict() {
     base::DictionaryValue* mutable_dict;
     if (events_value_.GetAsDictionary(&mutable_dict))
-      mutable_dict->Clear();
+      mutable_dict->DictClear();
     else
       NOTREACHED();
   }
diff --git a/chrome/browser/ash/policy/reporting/extension_install_event_log_uploader_unittest.cc b/chrome/browser/ash/policy/reporting/extension_install_event_log_uploader_unittest.cc
index b0739a20..d1d7805 100644
--- a/chrome/browser/ash/policy/reporting/extension_install_event_log_uploader_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/extension_install_event_log_uploader_unittest.cc
@@ -127,7 +127,7 @@
   void ClearReportDict() {
     base::DictionaryValue* mutable_dict;
     if (value_report_.GetAsDictionary(&mutable_dict))
-      mutable_dict->Clear();
+      mutable_dict->DictClear();
     else
       NOTREACHED();
   }
diff --git a/chrome/browser/browser_features.cc b/chrome/browser/browser_features.cc
index 1503f7c..9b3464bd 100644
--- a/chrome/browser/browser_features.cc
+++ b/chrome/browser/browser_features.cc
@@ -86,13 +86,6 @@
 #endif
 };
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-// Enables taking snapshots of the user data directory after a major
-// milestone update and restoring them after a version rollback.
-const base::Feature kUserDataSnapshot{"UserDataSnapshot",
-                                      base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-
 // Gates sandboxed iframe navigation toward external protocol behind any of:
 // - allow-popups
 // - allow-top-navigation
diff --git a/chrome/browser/browser_features.h b/chrome/browser/browser_features.h
index f8280bd6..8935581e 100644
--- a/chrome/browser/browser_features.h
+++ b/chrome/browser/browser_features.h
@@ -44,10 +44,6 @@
 
 extern const base::Feature kPwaUpdateDialogForNameAndIcon;
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-extern const base::Feature kUserDataSnapshot;
-#endif
-
 extern const base::Feature kSandboxExternalProtocolBlocked;
 extern const base::Feature kTabCaptureBlueBorder;
 extern const base::Feature kTabCaptureBlueBorderForSelfCaptureRegionCaptureOT;
diff --git a/chrome/browser/chromeos/extensions/external_cache_impl.cc b/chrome/browser/chromeos/extensions/external_cache_impl.cc
index 921fb35..5d56754 100644
--- a/chrome/browser/chromeos/extensions/external_cache_impl.cc
+++ b/chrome/browser/chromeos/extensions/external_cache_impl.cc
@@ -92,7 +92,7 @@
     // If list of know extensions is empty, don't init cache on disk. It is
     // important shortcut for test to don't wait forever for cache dir
     // initialization that should happen outside of Chrome on real device.
-    cached_extensions_->Clear();
+    cached_extensions_->DictClear();
     UpdateExtensionLoader();
     return;
   }
@@ -259,7 +259,7 @@
         url_loader_factory_, this, extensions::GetExternalVerifierFormat());
   }
 
-  cached_extensions_->Clear();
+  cached_extensions_->DictClear();
   for (const auto entry : extensions_->DictItems()) {
     if (!entry.second.is_dict()) {
       LOG(ERROR) << "ExternalCacheImpl found bad entry with type "
diff --git a/chrome/browser/chromeos/extensions/test_external_cache.cc b/chrome/browser/chromeos/extensions/test_external_cache.cc
index d12b023..13feb0e 100644
--- a/chrome/browser/chromeos/extensions/test_external_cache.cc
+++ b/chrome/browser/chromeos/extensions/test_external_cache.cc
@@ -33,7 +33,7 @@
   DCHECK(prefs);
 
   configured_extensions_ = std::move(prefs);
-  cached_extensions_.Clear();
+  cached_extensions_.DictClear();
 
   if (configured_extensions_->DictEmpty()) {
     delegate_->OnExtensionListsUpdated(&cached_extensions_);
diff --git a/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.cc b/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.cc
index 0332ab3..f989a30 100644
--- a/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/desk_template.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/desks_templates/desks_templates_client.h"
 #include "chrome/common/extensions/api/wm_desks_private.h"
 
@@ -121,6 +122,38 @@
           api_templates)));
 }
 
+WmDesksPrivateGetDeskTemplateJsonFunction::
+    WmDesksPrivateGetDeskTemplateJsonFunction() = default;
+WmDesksPrivateGetDeskTemplateJsonFunction::
+    ~WmDesksPrivateGetDeskTemplateJsonFunction() = default;
+
+ExtensionFunction::ResponseAction
+WmDesksPrivateGetDeskTemplateJsonFunction::Run() {
+  std::unique_ptr<api::wm_desks_private::GetDeskTemplateJson::Params> params(
+      api::wm_desks_private::GetDeskTemplateJson::Params::Create(args()));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  DesksTemplatesClient::Get()->GetTemplateJson(
+      params->template_uuid, Profile::FromBrowserContext(browser_context()),
+      base::BindOnce(
+          &WmDesksPrivateGetDeskTemplateJsonFunction::OnGetDeskTemplateJson,
+          this));
+  return did_respond() ? AlreadyResponded() : RespondLater();
+}
+
+void WmDesksPrivateGetDeskTemplateJsonFunction::OnGetDeskTemplateJson(
+    const std::string& template_json,
+    std::string error_string) {
+  if (!error_string.empty()) {
+    Respond(Error(std::move(error_string)));
+    return;
+  }
+
+  Respond(
+      ArgumentList(api::wm_desks_private::GetDeskTemplateJson::Results::Create(
+          template_json)));
+}
+
 WmDesksPrivateDeleteDeskTemplateFunction::
     WmDesksPrivateDeleteDeskTemplateFunction() = default;
 WmDesksPrivateDeleteDeskTemplateFunction::
diff --git a/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.h b/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.h
index 4589511..809b144 100644
--- a/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.h
+++ b/chrome/browser/chromeos/extensions/wm/wm_desks_private_api.h
@@ -78,6 +78,27 @@
       std::string error_string);
 };
 
+class WmDesksPrivateGetDeskTemplateJsonFunction : public ExtensionFunction {
+ public:
+  WmDesksPrivateGetDeskTemplateJsonFunction();
+  WmDesksPrivateGetDeskTemplateJsonFunction(
+      const WmDesksPrivateGetDeskTemplateJsonFunction&) = delete;
+  WmDesksPrivateGetDeskTemplateJsonFunction& operator=(
+      const WmDesksPrivateGetDeskTemplateJsonFunction&) = delete;
+
+  DECLARE_EXTENSION_FUNCTION("wmDesksPrivate.getDeskTemplateJson",
+                             WMDESKSPRIVATE_GETDESKTEMPLATEJSON)
+
+ protected:
+  ~WmDesksPrivateGetDeskTemplateJsonFunction() override;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+  void OnGetDeskTemplateJson(const std::string& template_json,
+                             std::string error_string);
+};
+
 class WmDesksPrivateDeleteDeskTemplateFunction : public ExtensionFunction {
  public:
   WmDesksPrivateDeleteDeskTemplateFunction();
diff --git a/chrome/browser/component_updater/pki_metadata_component_installer.cc b/chrome/browser/component_updater/pki_metadata_component_installer.cc
index 5eca4b15..b61f967 100644
--- a/chrome/browser/component_updater/pki_metadata_component_installer.cc
+++ b/chrome/browser/component_updater/pki_metadata_component_installer.cc
@@ -167,10 +167,31 @@
     log_ptr->public_key = decoded_key;
     // Operator history is ordered in inverse chronological order, so the 0th
     // element will be the current operator.
-    if (!log.operator_history().empty() &&
-        log.operator_history().Get(0).name() == kGoogleOperatorName) {
-      log_ptr->operated_by_google = true;
+    if (!log.operator_history().empty()) {
+      if (log.operator_history().Get(0).name() == kGoogleOperatorName) {
+        log_ptr->operated_by_google = true;
+      }
+      log_ptr->current_operator = log.operator_history().Get(0).name();
+      if (log.operator_history().size() > 1) {
+        // The protobuffer includes operator history in reverse chronological
+        // order, but we need it in chronological order, so we iterate in
+        // reverse (and ignoring the current operator).
+        for (auto it = log.operator_history().rbegin();
+             it != log.operator_history().rend() - 1; ++it) {
+          network::mojom::PreviousOperatorEntryPtr previous_operator =
+              network::mojom::PreviousOperatorEntry::New();
+          previous_operator->name = it->name();
+          // We use the next element's start time as the current element end
+          // time.
+          base::TimeDelta end_time =
+              base::Seconds((it + 1)->operator_start().seconds()) +
+              base::Nanoseconds((it + 1)->operator_start().nanos());
+          previous_operator->end_time = end_time;
+          log_ptr->previous_operators.push_back(std::move(previous_operator));
+        }
+      }
     }
+
     // State history is ordered in inverse chronological order, so the 0th
     // element will be the current state.
     if (!log.state().empty()) {
diff --git a/chrome/browser/devtools/devtools_settings.cc b/chrome/browser/devtools/devtools_settings.cc
index 97b6b53..556be6a 100644
--- a/chrome/browser/devtools/devtools_settings.cc
+++ b/chrome/browser/devtools/devtools_settings.cc
@@ -106,13 +106,13 @@
                                    kSyncDevToolsPreferencesDefault);
   DictionaryPrefUpdate unsynced_update(profile_->GetPrefs(),
                                        prefs::kDevToolsPreferences);
-  unsynced_update.Get()->Clear();
+  unsynced_update.Get()->DictClear();
   DictionaryPrefUpdate sync_enabled_update(
       profile_->GetPrefs(), prefs::kDevToolsSyncedPreferencesSyncEnabled);
-  sync_enabled_update.Get()->Clear();
+  sync_enabled_update.Get()->DictClear();
   DictionaryPrefUpdate sync_disabled_update(
       profile_->GetPrefs(), prefs::kDevToolsSyncedPreferencesSyncDisabled);
-  sync_disabled_update.Get()->Clear();
+  sync_disabled_update.Get()->DictClear();
 }
 
 const char* DevToolsSettings::GetDictionaryNameForSettingsName(
@@ -156,5 +156,5 @@
   target_update.Get()->MergeDictionary(
       profile_->GetPrefs()->GetDictionary(source_dictionary));
   DictionaryPrefUpdate source_update(profile_->GetPrefs(), source_dictionary);
-  source_update.Get()->Clear();
+  source_update.Get()->DictClear();
 }
diff --git a/chrome/browser/downgrade/downgrade_manager.cc b/chrome/browser/downgrade/downgrade_manager.cc
index 12ef65c..54f77dab 100644
--- a/chrome/browser/downgrade/downgrade_manager.cc
+++ b/chrome/browser/downgrade/downgrade_manager.cc
@@ -12,7 +12,6 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/enterprise_util.h"
-#include "base/feature_list.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -25,7 +24,6 @@
 #include "base/task/thread_pool.h"
 #include "base/version.h"
 #include "build/build_config.h"
-#include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/downgrade/downgrade_utils.h"
 #include "chrome/browser/downgrade/snapshot_manager.h"
@@ -154,15 +152,11 @@
 }
 
 bool UserDataSnapshotEnabled() {
-  if (g_snapshots_enabled_for_testing)
-    return true;
-  bool is_enterprise_managed =
+  return g_snapshots_enabled_for_testing ||
 #if defined(OS_WIN) || defined(OS_MAC)
-      base::IsMachineExternallyManaged() ||
+         base::IsMachineExternallyManaged() ||
 #endif
-      policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid();
-  return is_enterprise_managed &&
-         base::FeatureList::IsEnabled(features::kUserDataSnapshot);
+         policy::BrowserDMTokenStorage::Get()->RetrieveDMToken().is_valid();
 }
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index cfd03a7..fe926c1 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -184,8 +184,7 @@
     feature_list_.InitWithFeatures(
         /*enabled_features=*/
         {blink::features::kInterestGroupStorage,
-         blink::features::kAdInterestGroupAPI, blink::features::kFledge,
-         blink::features::kFencedFrames},
+         blink::features::kAdInterestGroupAPI, blink::features::kFledge},
         /*disabled_features=*/
         {});
     net::test_server::RegisterDefaultHandlers(embedded_test_server());
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
index eb180c0..86a7c3d 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -11,7 +11,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/extensions/api/input_method_private.h"
 #include "extensions/browser/extension_registry.h"
-#include "ui/base/ime/ash/ime_bridge.h"
 #include "ui/base/ime/ash/ime_keymap.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
@@ -241,9 +240,6 @@
   InputImeEventRouterFactory::GetInstance()->RemoveProfile(
       Profile::FromBrowserContext(browser_context_));
   EventRouter::Get(browser_context_)->UnregisterObserver(this);
-  if (observer_ && ui::IMEBridge::Get()) {
-    ui::IMEBridge::Get()->RemoveObserver(observer_.get());
-  }
 }
 
 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI>>::
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.h b/chrome/browser/extensions/api/input_ime/input_ime_api.h
index 139039b8..77195e7 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api.h
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api.h
@@ -26,7 +26,6 @@
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/extension.h"
-#include "ui/base/ime/ash/ime_bridge_observer.h"
 #include "ui/base/ime/ash/ime_engine_handler_interface.h"
 #include "ui/base/ime/text_input_flags.h"
 
@@ -142,8 +141,6 @@
   // Listen to extension load, unloaded notifications.
   base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
       extension_registry_observation_{this};
-
-  std::unique_ptr<ui::IMEBridgeObserver> observer_;
 };
 
 template <>
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
index 34fd3c6..19a536bc 100644
--- a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
@@ -102,7 +102,7 @@
   // The api permissions don't need to be present either.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     value->SetKey("origins", origins->Clone());
     EXPECT_TRUE(Permissions::Populate(*value, &permissions_object));
 
@@ -119,7 +119,7 @@
   // Throw errors for non-string API permissions.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     base::Value invalid_apis = apis->Clone();
     invalid_apis.Append(3);
     value->SetKey("permissions", std::move(invalid_apis));
@@ -129,7 +129,7 @@
   // Throw errors for non-string origins.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     base::Value invalid_origins = origins->Clone();
     invalid_origins.Append(3);
     value->SetKey("origins", std::move(invalid_origins));
@@ -139,14 +139,14 @@
   // Throw errors when "origins" or "permissions" are not list values.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     value->Set("origins", std::make_unique<base::Value>(2));
     EXPECT_FALSE(Permissions::Populate(*value, &permissions_object));
   }
 
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     value->Set("permissions", std::make_unique<base::Value>(2));
     EXPECT_FALSE(Permissions::Populate(*value, &permissions_object));
   }
@@ -154,7 +154,7 @@
   // Additional fields should be allowed.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     value->SetKey("origins", origins->Clone());
     value->SetKey("random", base::Value(3));
     EXPECT_TRUE(Permissions::Populate(*value, &permissions_object));
@@ -172,7 +172,7 @@
   // Unknown permissions should throw an error.
   {
     Permissions permissions_object;
-    value->Clear();
+    value->DictClear();
     base::Value invalid_apis = apis->Clone();
     invalid_apis.Append("unknown_permission");
     value->SetKey("permissions", std::move(invalid_apis));
diff --git a/extensions/browser/app_window/app_window_interactive_uitest.cc b/chrome/browser/extensions/app_window_uitest.cc
similarity index 100%
rename from extensions/browser/app_window/app_window_interactive_uitest.cc
rename to chrome/browser/extensions/app_window_uitest.cc
diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
index 6cb583a..aa391e2 100644
--- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc
@@ -53,15 +53,12 @@
 #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h"
 #include "ppapi/buildflags/buildflags.h"
 
-#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_WIN)
-#include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
-#endif
-
 #if defined(OS_LINUX) || defined(OS_MAC) || defined(OS_WIN)
 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
 #include "chrome/browser/extensions/api/platform_keys/verify_trust_api.h"
 #include "chrome/browser/extensions/api/terminal/terminal_private_api.h"
 #endif
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3d15ff83..4e0d32d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1481,21 +1481,15 @@
     "expiry_milestone": 97
   },
   {
-    "name": "enable-autofill-credit-card-ablation-experiment",
-    "owners": [ "jsaul@google.com" ],
-    "expiry_milestone": 96
-  },
-  {
     "name": "enable-autofill-credit-card-authentication",
     "owners": [ "jsaul@google.com", "siyua" ],
-    "expiry_milestone": 97
+    "expiry_milestone": 105
   },
   {
     "name": "enable-autofill-credit-card-upload",
     "owners": [ "jsaul@google.com" ],
     // This flag is heavily used by the testing team that can't easily use the
-    // commandline, and can't be enabled by default.
-    // http://g/chrome-flags/s2RTQCvcRRs
+    // commandline, especially in India.
     "expiry_milestone": -1
   },
   {
@@ -1971,12 +1965,12 @@
   },
   {
     "name": "enable-experimental-accessibility-switch-access-setup-guide",
-    "owners": ["ansatasi@google.com", "josiahk@google.com", "//ui/accessibility/OWNERS"],
+    "owners": [ "josiahk@google.com", "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 97
   },
   {
     "name": "enable-experimental-accessibility-switch-access-text",
-    "owners": [ "anastasi@google.com", "//ui/accessibility/OWNERS" ],
+    "owners": [ "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 95
   },
   {
@@ -2655,7 +2649,7 @@
   },
   {
     "name": "enable-store-hours",
-    "owners": [ "jawaid@google.com" ],
+    "owners": [ "meiliang@chromium.org" ],
     "expiry_milestone": 97
   },
   {
@@ -2808,11 +2802,6 @@
     "expiry_milestone": 80
   },
   {
-    "name": "enable-user-data-snapshot",
-    "owners": [ "ydago", "grt" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "enable-vaapi-av1-decode-acceleration",
     "owners": [ "andrescj", "chromeos-gfx-video@google.com" ],
     "expiry_milestone": 100
@@ -3066,7 +3055,7 @@
   {
     "name": "explore-sites",
     "owners": [ "chili", "dewittj" ],
-    "expiry_milestone": 95
+    "expiry_milestone": 102
   },
   {
     "name": "extension-content-verification",
@@ -4809,7 +4798,7 @@
   },
   {
     "name": "scan-app-searchable-pdf",
-    "owners": [ "kmoed", "cros-peripherals@google.com" ],
+    "owners": [ "kmoed@google.com", "cros-peripherals@google.com" ],
     "expiry_milestone": 97
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 83ab904..553afa86 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5372,13 +5372,6 @@
 #endif  // BUILDFLAG(USE_TCMALLOC)
 #endif  // #if defined(OS_CHROMEOS) || defined(OS_LINUX)
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-const char kUserDataSnapshotName[] = "Enable user data snapshots";
-const char kUserDataSnapshotDescription[] =
-    "Enables taking snapshots of the user data directory after a Chrome "
-    "update and restoring them after a version rollback.";
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-
 #if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MAC)
 const char kWebShareName[] = "Web Share";
 const char kWebShareDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9ff4a335..671951fa 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3123,11 +3123,6 @@
 #endif  // BUILDFLAG(USE_TCMALLOC)
 #endif  // #if defined(OS_CHROMEOS) || defined(OS_LINUX)
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-extern const char kUserDataSnapshotName[];
-extern const char kUserDataSnapshotDescription[];
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-
 #if defined(OS_WIN) || defined(OS_CHROMEOS) || defined(OS_MAC)
 extern const char kWebShareName[];
 extern const char kWebShareDescription[];
diff --git a/chrome/browser/google/google_brand_code_map_chromeos.cc b/chrome/browser/google/google_brand_code_map_chromeos.cc
index 40169df..b9adfcc 100644
--- a/chrome/browser/google/google_brand_code_map_chromeos.cc
+++ b/chrome/browser/google/google_brand_code_map_chromeos.cc
@@ -48,6 +48,7 @@
                      {"ACBF", {"SSNP", "VHIH", "QMFD"}},
                      {"ADGK", {"PKUQ", "AEMI", "CUUL"}},
                      {"ADID", {"XDMY", "QHTP", "PBND"}},
+                     {"AGVY", {"RNNC", "KYLA", "NJOS"}},
                      {"AJIM", {"XQAQ", "WFLV", "AMBR"}},
                      {"ALRH", {"XDKE", "TDIH", "VLER"}},
                      {"ANAE", {"IWTJ", "CISE", "SLJZ"}},
@@ -79,6 +80,7 @@
                      {"BAUA", {"UWIF", "EOEW", "RPDR"}},
                      {"BCOL", {"YJDV", "GSIC", "BAUL"}},
                      {"BDIW", {"UDUG", "TRYQ", "PWFV"}},
+                     {"BDXJ", {"EWPX", "PXLS", "LPDD"}},
                      {"BKLL", {"DJXO", "KLUN", "DJNO"}},
                      {"BLXA", {"VLID", "JNUQ", "IKRB"}},
                      {"BMAD", {"HGZG", "AOPW", "RIVV"}},
@@ -110,6 +112,7 @@
                      {"DEAH", {"HRBU", "DJKF", "CMPZ"}},
                      {"DHAS", {"KEDN", "LUZR", "MHFN"}},
                      {"DISZ", {"PPAR", "VCPW", "NJKK"}},
+                     {"DJBB", {"ZLXN", "WQCE", "ASCQ"}},
                      {"DKJM", {"VRGL", "PZYF", "VBTW"}},
                      {"DRYI", {"LWTQ", "OLEY", "NWUA"}},
                      {"DSCL", {"OSET", "BPKO", "KRIN"}},
@@ -303,6 +306,7 @@
                      {"LOGF", {"OWNI", "ECYV", "JEFV"}},
                      {"LOGH", {"RTVE", "EJJV", "DNTX"}},
                      {"LOGI", {"OEYI", "IKUX", "TCEI"}},
+                     {"LPEW", {"XBJZ", "HTBP", "JQXK"}},
                      {"LULQ", {"DEHI", "QYXC", "KAGT"}},
                      {"LYFT", {"LMQF", "CYMI", "ZGEF"}},
                      {"LYLN", {"XXWY", "JEUV", "RSOC"}},
@@ -314,6 +318,7 @@
                      {"MDPZ", {"AHBA", "ENTF", "IIMC"}},
                      {"MEXL", {"JFMC", "LBVP", "DERH"}},
                      {"MNFK", {"BFMJ", "APMV", "LPJQ"}},
+                     {"MNQW", {"LCRH", "YVGU", "SJID"}},
                      {"MNZG", {"PPTP", "OFXE", "ROJJ"}},
                      {"MOIP", {"HCCZ", "PXCU", "MROE"}},
                      {"MQUZ", {"MFAZ", "GBNW", "MRMS"}},
@@ -324,6 +329,7 @@
                      {"MZVS", {"VUZM", "RIDT", "URTS"}},
                      {"NAMM", {"BFSS", "BKVK", "EBDV"}},
                      {"NBQS", {"KMJF", "MFWA", "UWRX"}},
+                     {"NGVJ", {"GVZG", "GJWP", "CFNU"}},
                      {"NISD", {"MISA", "YDPG", "NCLQ"}},
                      {"NMOG", {"UYQU", "ZWTV", "TQFQ"}},
                      {"NOMD", {"GZLV", "UNZR", "FVOP"}},
@@ -383,6 +389,7 @@
                      {"SBBR", {"IMRL", "LZCR", "WJQV"}},
                      {"SBGV", {"ZNIN", "ZVZV", "BPJY"}},
                      {"SFGV", {"TSMJ", "SVHE", "WNOP"}},
+                     {"SGGB", {"HSKN", "BECX", "NFTY"}},
                      {"SHAN", {"OERN", "XNHK", "GVYX"}},
                      {"SKIW", {"CLPF", "OTYY", "ZJVP"}},
                      {"SMAC", {"FDEX", "ZFXY", "DJMW"}},
@@ -400,7 +407,9 @@
                      {"SSVR", {"NZKV", "NGLW", "LDCH"}},
                      {"STMU", {"HKNS", "OFBT", "RWDO"}},
                      {"SUCA", {"JSZT", "IBUF", "HMEZ"}},
+                     {"SVGZ", {"WWDD", "EJWL", "TJFT"}},
                      {"SWLP", {"GLDC", "WZKJ", "GTXT"}},
+                     {"SYDL", {"CGGV", "VDEY", "UZDR"}},
                      {"TAAB", {"ZBMY", "NYDT", "CXYZ"}},
                      {"TAAC", {"YBVP", "RXXN", "HMDY"}},
                      {"TBKT", {"IBUN", "QLQQ", "CRBQ"}},
diff --git a/chrome/browser/media/router/discovery/access_code/OWNERS b/chrome/browser/media/router/discovery/access_code/OWNERS
index 21a5668..d6072ff7 100644
--- a/chrome/browser/media/router/discovery/access_code/OWNERS
+++ b/chrome/browser/media/router/discovery/access_code/OWNERS
@@ -1,5 +1 @@
-# ChromeOS EDU Team Members
-gbj@google.com
-bzielinski@google.com
-bmalcolm@google.com
-jacqueli@googlle.com
+file://chrome/browser/ui/webui/access_code_cast/OWNERS
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index 1511cb3cb..f22af2d 100644
--- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -897,73 +897,4 @@
                     capturing_tab->GetMainFrame()->GetLastCommittedOrigin(),
                     url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
 }
-
-IN_PROC_BROWSER_TEST_F(GetDisplayMediaChangeSourceBrowserTest,
-                       ChangeSourceReject) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  content::WebContents* captured_tab = OpenTestPageInNewTab(kCapturedPageMain);
-  content::WebContents* other_tab = OpenTestPageInNewTab(kMainHtmlPage);
-  content::WebContents* capturing_tab = OpenTestPageInNewTab(kMainHtmlPage);
-
-  RunGetDisplayMedia(capturing_tab, "{video: true}", /*is_fake_ui=*/false,
-                     /*expect_success=*/true,
-                     /*is_tab_capture=*/true);
-  while (browser()->tab_strip_model()->GetActiveWebContents() != captured_tab) {
-    base::RunLoop().RunUntilIdle();
-  }
-
-  EXPECT_TRUE(captured_tab->IsBeingCaptured());
-  EXPECT_FALSE(other_tab->IsBeingCaptured());
-  EXPECT_FALSE(capturing_tab->IsBeingCaptured());
-  EXPECT_EQ(GetSecondaryButtonLabel(captured_tab),
-            l10n_util::GetStringFUTF16(
-                IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON,
-                url_formatter::FormatOriginForSecurityDisplay(
-                    captured_tab->GetMainFrame()->GetLastCommittedOrigin(),
-                    url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
-  EXPECT_EQ(GetSecondaryButtonLabel(other_tab), kShareThisTabInsteadMessage);
-  EXPECT_EQ(GetSecondaryButtonLabel(capturing_tab),
-            l10n_util::GetStringFUTF16(
-                IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON,
-                url_formatter::FormatOriginForSecurityDisplay(
-                    capturing_tab->GetMainFrame()->GetLastCommittedOrigin(),
-                    url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
-
-  browser()->tab_strip_model()->ActivateTabAt(
-      browser()->tab_strip_model()->GetIndexOfWebContents(other_tab));
-  while (browser()->tab_strip_model()->GetActiveWebContents() != other_tab) {
-    base::RunLoop().RunUntilIdle();
-  }
-
-  browser()->profile()->GetPrefs()->SetBoolean(prefs::kScreenCaptureAllowed,
-                                               false);
-
-  // Click the secondary button, i.e., the "Share this tab instead" button. This
-  // is rejected since screen capture is not allowed by the above policy.
-  GetDelegate(other_tab)->Cancel();
-
-  // When "Share this tab instead" fails for other_tab, the focus goes back to
-  // the captured tab. Wait until that happens:
-  while (browser()->tab_strip_model()->GetActiveWebContents() != captured_tab) {
-    base::RunLoop().RunUntilIdle();
-  }
-
-  EXPECT_TRUE(captured_tab->IsBeingCaptured());
-  EXPECT_FALSE(other_tab->IsBeingCaptured());
-  EXPECT_FALSE(capturing_tab->IsBeingCaptured());
-  EXPECT_EQ(GetSecondaryButtonLabel(captured_tab),
-            l10n_util::GetStringFUTF16(
-                IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON,
-                url_formatter::FormatOriginForSecurityDisplay(
-                    captured_tab->GetMainFrame()->GetLastCommittedOrigin(),
-                    url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
-  EXPECT_EQ(GetSecondaryButtonLabel(other_tab), kShareThisTabInsteadMessage);
-  EXPECT_EQ(GetSecondaryButtonLabel(capturing_tab),
-            l10n_util::GetStringFUTF16(
-                IDS_TAB_SHARING_INFOBAR_SWITCH_TO_BUTTON,
-                url_formatter::FormatOriginForSecurityDisplay(
-                    capturing_tab->GetMainFrame()->GetLastCommittedOrigin(),
-                    url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)));
-}
-
 #endif
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index c875f19..42c6905 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -522,6 +522,7 @@
       network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
       log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length);
       log_info->name = ct_log.log_name;
+      log_info->current_operator = ct_log.current_operator;
 
       std::string log_id = crypto::SHA256HashString(log_info->public_key);
       log_info->operated_by_google =
@@ -535,6 +536,16 @@
       if (it != std::end(disqualified_logs) && it->first == log_id) {
         log_info->disqualified_at = it->second;
       }
+
+      for (size_t i = 0; i < ct_log.previous_operators_length; i++) {
+        const auto& op = ct_log.previous_operators[i];
+        network::mojom::PreviousOperatorEntryPtr previous_operator =
+            network::mojom::PreviousOperatorEntry::New();
+        previous_operator->name = op.name;
+        previous_operator->end_time = op.end_time;
+        log_info->previous_operators.push_back(std::move(previous_operator));
+      }
+
       log_list_mojo.push_back(std::move(log_info));
     }
     network_service->UpdateCtLogList(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 27e9d8a0..6e17165 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -590,13 +590,8 @@
   base::HistogramTester histogram_tester;
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_with_hints()));
-  EXPECT_EQ(optimization_guide::RetryForHistogramUntilCountReached(
-                &histogram_tester, "OptimizationGuide.LoadedHint.Result", 1),
-            1);
-  // There were no hints that match this URL, but there should still be an
-  // attempt to load a hint but still fail.
-  histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
-                                      false, 1);
+  optimization_guide::RetryForHistogramUntilCountReached(
+      &histogram_tester, "OptimizationGuide.LoadedHint.Result", 1);
 
   std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
   base::flat_set<GURL> received_callbacks;
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 19de1be..faff392 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -97,7 +97,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
-#endif  // ENABLE_DICE_SUPPORT
+#endif  // BUIDLFLAG(ENABLE_DICE_SUPPORT)
 
 using autofill::ParsingResult;
 using base::ASCIIToUTF16;
@@ -4052,7 +4052,7 @@
   EXPECT_TRUE(prompt_observer.IsSavePromptAvailable());
 }
 
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 // This test suite only applies to Gaia signin page, and checks that the
 // signin interception bubble and the password bubbles never conflict.
 class PasswordManagerBrowserTestWithSigninInterception
@@ -4225,7 +4225,7 @@
   FillAndSubmitGaiaPassword();
   EXPECT_FALSE(prompt_observer.IsSavePromptShownAutomatically());
 }
-#endif  // ENABLE_DICE_SUPPORT && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUIDLFLAG(ENABLE_DICE_SUPPORT)
 
 class TestPasswordManagerClient : public ChromePasswordManagerClient {
  public:
diff --git a/chrome/browser/password_manager/password_manager_interactive_uitest.cc b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
index 1fcd15c..d531cc13 100644
--- a/chrome/browser/password_manager/password_manager_interactive_uitest.cc
+++ b/chrome/browser/password_manager/password_manager_interactive_uitest.cc
@@ -29,14 +29,14 @@
 #include "content/public/test/browser_test_utils.h"
 #include "third_party/blink/public/common/switches.h"
 
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "chrome/browser/password_manager/password_manager_signin_intercept_test_helper.h"
 #include "chrome/browser/signin/dice_web_signin_interceptor.h"
-#endif  // ENABLE_DICE_SUPPORT
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 namespace {
 
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 // Wait until |condition| returns true.
 void WaitForCondition(base::RepeatingCallback<bool()> condition) {
   while (!condition.Run()) {
@@ -46,7 +46,7 @@
     run_loop.Run();
   }
 }
-#endif  // ENABLE_DICE_SUPPORT
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 }  // namespace
 
@@ -494,9 +494,7 @@
   }
 }
 
-// TODO(crbug.com/1198490): Remove explicit !BUILDFLAG(IS_CHROMEOS_LACROS) when
-// DICE is not enabled on Lacros.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 // This test suite only applies to Gaia signin page, and checks that the
 // signin interception bubble and the password bubbles never conflict.
 class PasswordManagerInteractiveTestWithSigninInterception
@@ -580,6 +578,6 @@
   navigation_observer.Wait();
   EXPECT_TRUE(prompt_observer.IsUpdatePromptShownAutomatically());
 }
-#endif  // ENABLE_DICE_SUPPORT
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 }  // namespace password_manager
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index b36deaf..01e297ee3 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -195,11 +195,26 @@
   }
 
   if (is_chromeos_ash) {
+    sources += [
+      "test/accessibility_policy_browsertest.cc",
+      "test/arc_policy_browsertest.cc",
+      "test/assistant_policy_browsertest.cc",
+      "test/audio_output_allowed_browsertest.cc",
+      "test/session_length_limit_policy_browsertest.cc",
+      "test/suggested_content_policy_browsertest.cc",
+      "test/unified_desktop_enabled_browsertest.cc",
+    ]
+
     deps += [
+      "//ash/components/arc:arc_test_support",
+      "//ash/components/arc:prefs",
+      "//ash/components/arc/session:session",
+      "//ash/components/audio:audio",
       "//chrome/browser/chromeos",
       "//chromeos/cryptohome",
       "//chromeos/dbus/constants",
       "//chromeos/dbus/userdataauth",
+      "//chromeos/services/assistant/public/cpp",
       "//components/account_id",
     ]
   }
@@ -218,15 +233,20 @@
   if (is_win) {
     sources += [
       "test/audio_process_high_priority_enabled_browsertest.cc",
+      "test/locale_policy_browsertest.cc",
       "test/network_service_sandbox_enabled_browsertest.cc",
     ]
   }
 
   if (!is_android) {
-    sources += [ "cloud/component_cloud_policy_browsertest.cc" ]
+    sources += [
+      "cloud/component_cloud_policy_browsertest.cc",
+      "test/promotional_tabs_enabled_policy_browsertest.cc",
+    ]
 
     deps += [
       "//chrome/browser/ui:ui",
+      "//chrome/common:version_header",
       "//chrome/test:test_support",
     ]
   }
@@ -244,6 +264,11 @@
     sources += [
       "cloud/chrome_browser_cloud_management_browsertest_delegate_desktop.cc",
       "cloud/chrome_browser_cloud_management_browsertest_delegate_desktop.h",
+      "test/hardware_acceleration_mode_enabled_browsertest.cc",
     ]
   }
+
+  if (!is_android && !is_mac) {
+    sources += [ "test/full_screen_allowed_policy_browsertest.cc" ]
+  }
 }
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index f154efb..0de0532 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -500,7 +500,7 @@
     pdf_conversion_state_->OnPageProcessed(
         base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
   }
-  document_->DropPage(page);
+  document_->RemovePage(page);
 }
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/privacy/secure_dns_bridge.cc b/chrome/browser/privacy/secure_dns_bridge.cc
index 1727c24b..2ad912b 100644
--- a/chrome/browser/privacy/secure_dns_bridge.cc
+++ b/chrome/browser/privacy/secure_dns_bridge.cc
@@ -4,12 +4,17 @@
 
 #include <jni.h>
 
+#include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/dns_probe_runner.h"
 #include "chrome/browser/net/secure_dns_config.h"
@@ -20,19 +25,10 @@
 #include "chrome/common/pref_names.h"
 #include "components/country_codes/country_codes.h"
 #include "components/prefs/pref_service.h"
-#include "net/dns/public/doh_provider_entry.h"
-#include "net/dns/public/secure_dns_mode.h"
-#include "net/dns/public/util.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/callback_helpers.h"
-#include "base/task/post_task.h"
-#include "base/task/thread_pool.h"
-
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_service_instance.h"
+#include "net/dns/public/doh_provider_entry.h"
+#include "net/dns/public/secure_dns_mode.h"
 
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 20032900..01cc5f1 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -489,9 +489,7 @@
   SigninProfileAttributesUpdaterFactory::GetInstance();
   if (site_engagement::SiteEngagementService::IsEnabled())
     site_engagement::SiteEngagementServiceFactory::GetInstance();
-// TODO(https://crbug.com/1198523: Remove Lacros check once Dice is no longer
-// supported on Lacros.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
   SigninManagerFactory::GetInstance();
 #endif
 #if BUILDFLAG(ENABLE_SPELLCHECK)
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
index 3ecf23b..bcd35d5 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
@@ -526,7 +526,7 @@
             expected_metrics);
 
   // Replace the tab.
-  content::WebContents::CreateParams web_contents_params(profile(), nullptr);
+  content::WebContents::CreateParams web_contents_params(profile());
   std::unique_ptr<content::WebContents> new_contents = base::WrapUnique(
       content::WebContentsTester::CreateTestWebContents(web_contents_params));
   std::unique_ptr<content::WebContents> old_contents =
diff --git a/chrome/browser/resources/access_code_cast/BUILD.gn b/chrome/browser/resources/access_code_cast/BUILD.gn
index 155fe27c..08c53a4 100644
--- a/chrome/browser/resources/access_code_cast/BUILD.gn
+++ b/chrome/browser/resources/access_code_cast/BUILD.gn
@@ -12,7 +12,10 @@
 resources_grd_file = "$target_gen_dir/resources.grd"
 
 html_to_js("web_components") {
-  js_files = [ "access_code_cast.ts" ]
+  js_files = [
+    "access_code_cast.ts",
+    "code_input/code_input.ts",
+  ]
 }
 
 copy("copy_browser_proxy") {
@@ -50,9 +53,10 @@
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
   in_files = [
-    "browser_proxy.ts",
     "access_code_cast.mojom-webui.js",
     "access_code_cast.ts",
+    "browser_proxy.ts",
+    "code_input/code_input.ts",
     "route_request_result_code.mojom-webui.js",
   ]
 }
diff --git a/chrome/browser/resources/access_code_cast/OWNERS b/chrome/browser/resources/access_code_cast/OWNERS
index 85d2109..ccb161d 100644
--- a/chrome/browser/resources/access_code_cast/OWNERS
+++ b/chrome/browser/resources/access_code_cast/OWNERS
@@ -1,4 +1 @@
-gbj@google.com
-bmalcolm@chromium.org
-bzielinski@google.com
-jacqueli@google.com
\ No newline at end of file
+file://chrome/browser/ui/webui/access_code_cast/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/resources/access_code_cast/access_code_cast.html b/chrome/browser/resources/access_code_cast/access_code_cast.html
index 5b6a4228..6aed44b2 100644
--- a/chrome/browser/resources/access_code_cast/access_code_cast.html
+++ b/chrome/browser/resources/access_code_cast/access_code_cast.html
@@ -1,4 +1,4 @@
-<style>
+<style include="cr-shared-style">
   .button-image {
     margin-inline-end: 8px;
   }
@@ -18,19 +18,25 @@
     margin: 32px 0;
   }
 </style>
-<h1>Cast to a new display</h1>
+<h1>$i18n{dialogTitle}</h1>
 <div id="codeInputView">
-  <p>Typed input view</p>
+  <c2c-code-input length="6" value="" id="codeInput"></c2c-code-input>
   <cr-button on-click="switchToQrInput" class="text-button">
     <iron-icon class="button-image" icon="cr:videocam"></iron-icon>
-    Use camera instead
+    $i18n{useCamera}
   </cr-button>
 </div>
 <div id="qrInputView">
   <p>Camera input view</p>
 </div>
 <div id="button-container">
-  <cr-button on-click="close">Close</cr-button>
-  <cr-button id="castButton" class="action-button">Cast</cr-button>
-  <cr-button id="backButton" on-click="switchToCodeInput" class="action-button">Back</cr-button>
+  <cr-button on-click="close">$i18n{close}</cr-button>
+  <cr-button id="castButton" class="action-button"
+    disabled="[[castButtonDisabled]]">
+    $i18n{cast}
+  </cr-button>
+  <cr-button id="backButton" on-click="switchToCodeInput"
+    class="action-button">
+    $i18n{back}
+  </cr-button>
 </div>
diff --git a/chrome/browser/resources/access_code_cast/access_code_cast.ts b/chrome/browser/resources/access_code_cast/access_code_cast.ts
index 7656ceaa..e052ab34 100644
--- a/chrome/browser/resources/access_code_cast/access_code_cast.ts
+++ b/chrome/browser/resources/access_code_cast/access_code_cast.ts
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './code_input/code_input.js';
+
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import 'chrome://resources/cr_elements/icons.m.js';
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
@@ -12,6 +14,7 @@
 
 import {PageCallbackRouter} from './access_code_cast.mojom-webui.js';
 import {BrowserProxy} from './browser_proxy.js';
+import {CodeInputElement} from './code_input/code_input.js';
 
 declare const chrome: any;
 
@@ -25,6 +28,7 @@
     backButton: CrButtonElement;
     castButton: CrButtonElement;
     codeInputView: HTMLDivElement;
+    codeInput: CodeInputElement;
     qrInputView: HTMLDivElement;
   }
 }
@@ -41,15 +45,24 @@
   private listenerIds: Array<number>;
   private router: PageCallbackRouter;
 
+  codeLength: number;
+  castButtonDisabled: boolean;
+
   constructor() {
     super();
     this.listenerIds = [];
     this.router = BrowserProxy.getInstance().callbackRouter;
+
+    this.codeLength = 6;
+    this.castButtonDisabled = true;
   }
 
   ready() {
     super.ready();
     this.setState(PageState.CODE_INPUT);
+    this.$.codeInput.addEventListener('access-code-input', (e: any) => {
+      this.handleCodeInput(e);
+    });
   }
 
   connectedCallback() {
@@ -78,6 +91,15 @@
     this.$.castButton.hidden = state !== PageState.CODE_INPUT;
     this.$.qrInputView.hidden = state !== PageState.QR_INPUT;
     this.$.backButton.hidden = state !== PageState.QR_INPUT;
+
+    if (state === PageState.CODE_INPUT) {
+      this.$.codeInput.clearInput();
+      this.$.codeInput.focusInput();
+    }
+  }
+
+  private handleCodeInput(e: any) {
+    this.castButtonDisabled = e.detail.value.length !== this.codeLength;
   }
 }
 
diff --git a/chrome/browser/resources/access_code_cast/code_input/code_input.html b/chrome/browser/resources/access_code_cast/code_input/code_input.html
new file mode 100644
index 0000000..6b11310
--- /dev/null
+++ b/chrome/browser/resources/access_code_cast/code_input/code_input.html
@@ -0,0 +1,14 @@
+<style>
+  cr-input {
+    font-size: 50px;
+    margin: auto;
+    width: 300px;
+  }
+</style>
+<cr-input 
+  type="text" 
+  minlength="[[length]]" 
+  maxlength="[[length]]" 
+  id="accessCodeInput" 
+  value="{{value}}">
+</cr-input>
\ No newline at end of file
diff --git a/chrome/browser/resources/access_code_cast/code_input/code_input.ts b/chrome/browser/resources/access_code_cast/code_input/code_input.ts
new file mode 100644
index 0000000..67f4951
--- /dev/null
+++ b/chrome/browser/resources/access_code_cast/code_input/code_input.ts
@@ -0,0 +1,64 @@
+// 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.
+
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+export interface CodeInputElement {
+  $: {
+    accessCodeInput: CrInputElement;
+  }
+}
+
+export class CodeInputElement extends PolymerElement {
+  static get is() {
+    return 'c2c-code-input';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      length: Number,
+      value: {
+        type: String,
+        value: '',
+      }
+    };
+  }
+
+  get crInput() {
+    return this.$.accessCodeInput;
+  }
+
+  value: string;
+
+  ready() {
+    super.ready();
+    this.$.accessCodeInput.addEventListener('input', () => {
+      this.handleInput();
+    });
+  }
+
+  clearInput() {
+    this.$.accessCodeInput.value = '';
+  }
+
+  focusInput() {
+    this.$.accessCodeInput.focusInput();
+  }
+
+  private handleInput() {
+    this.$.accessCodeInput.value = this.$.accessCodeInput.value.toUpperCase();
+    this.dispatchEvent(new CustomEvent('access-code-input', {
+      detail: {value: this.$.accessCodeInput.value}
+    }));
+  }
+}
+
+customElements.define(CodeInputElement.is, CodeInputElement);
\ No newline at end of file
diff --git a/chrome/browser/resources/access_code_cast/index.html b/chrome/browser/resources/access_code_cast/index.html
index c120584..001fc21b 100644
--- a/chrome/browser/resources/access_code_cast/index.html
+++ b/chrome/browser/resources/access_code_cast/index.html
@@ -3,8 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title>Cast Receiver</title>
-  <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <script type="module" src="access_code_cast.js"></script>
 </head>
 
diff --git a/chrome/browser/resources/components/components.html b/chrome/browser/resources/components/components.html
index 7361019..45678b1 100644
--- a/chrome/browser/resources/components/components.html
+++ b/chrome/browser/resources/components/components.html
@@ -5,10 +5,27 @@
 <title>$i18n{componentsTitle}</title>
 <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
 <link rel="stylesheet" href="components.css">
+<if expr="lacros or chromeos">
+  <link rel="stylesheet" href="chrome://resources/css/os_header.css">
+</if>
+
 </head>
 <body>
 <div id="body-container" style="visibility:hidden">
 
+  <if expr="lacros or chromeos">
+    <div class="os-link-container-container" id="os-link-container">
+      <div class="os-link-container">
+        <span class="os-link-icon"></span>
+        <span aria-hidden="true" id="os-link-desc">$i18n{os-components-text1}</span>
+        <a href="#" id="os-link-href" aria-describedby="os-link-desc">
+          $i18n{os-components-link}
+        </a>
+        <span aria-hidden="true">$i18n{os-components-text2}</span>
+      </div>
+    </div>
+  </if>
+
   <div id="header"><h1>$i18n{componentsTitle}</h1></div>
   <div id="component-placeholder"></div>
   <div id="component-template" hidden>
diff --git a/chrome/browser/resources/components/components.js b/chrome/browser/resources/components/components.js
index e761310..757401a3 100644
--- a/chrome/browser/resources/components/components.js
+++ b/chrome/browser/resources/components/components.js
@@ -30,8 +30,24 @@
   $('component-placeholder').appendChild(output);
   jstProcess(input, output);
   output.removeAttribute('hidden');
+
+// <if expr="chromeos or lacros">
+  const crosUrlRedirectButton = $('os-link-href');
+  if (crosUrlRedirectButton) {
+    crosUrlRedirectButton.onclick = crosUrlComponentRedirect;
+  }
+// </if>
 }
 
+// <if expr="chromeos or lacros">
+/**
+ * Called when the user clicks on the os-link-href button.
+ */
+function crosUrlComponentRedirect() {
+  chrome.send('crosUrlComponentsRedirect');
+}
+// </if>
+
 /**
  * Asks the C++ ComponentsDOMHandler to get details about the installed
  * components.
@@ -45,7 +61,8 @@
  * current state of installed components. The componentsData will also be
  * stored in currentComponentsData to be available to JS for testing purposes.
  * @param {{
- *   components: !Array<!{name: string, version: string}>
+ *   components: !Array<!{name: string, version: string}>,
+ *   showOsLink: Boolean
  * }} componentsData Detailed info about installed components. The template
  * expects each component's format to match the following structure to correctly
  *     populate the page:
@@ -91,6 +108,11 @@
     });
   }
 
+  const systemFlagsLinkDiv = $('os-link-container');
+  if (systemFlagsLinkDiv) {
+    systemFlagsLinkDiv.hidden = !componentsData.showOsLink;
+  }
+
   bodyContainer.style.visibility = 'visible';
   body.className = 'show-tmi-mode-initial';
 }
diff --git a/chrome/browser/resources/history/history_list.html b/chrome/browser/resources/history/history_list.html
index 3d63e40..8b1a905 100644
--- a/chrome/browser/resources/history/history_list.html
+++ b/chrome/browser/resources/history/history_list.html
@@ -47,13 +47,14 @@
     <cr-lazy-render id="dialog">
       <template>
         <cr-dialog consume-keydown-event>
-          <div slot="title">$i18n{removeSelected}</div>
-          <div slot="body">$i18n{deleteWarning}</div>
+          <div slot="title" id="title">$i18n{removeSelected}</div>
+          <div slot="body" id="body">$i18n{deleteWarning}</div>
           <div slot="button-container">
             <cr-button class="cancel-button" on-click="onDialogCancelTap_">
               $i18n{cancel}
             </cr-button>
-            <cr-button class="action-button" on-click="onDialogConfirmTap_">
+            <cr-button class="action-button" on-click="onDialogConfirmTap_"
+                <if expr="is_macosx">aria-describedby="title body"</if>>
               $i18n{deleteConfirm}
             </cr-button>
           </div>
diff --git a/chrome/browser/resources/tab_search/app.ts b/chrome/browser/resources/tab_search/app.ts
index 14b913a..862a754 100644
--- a/chrome/browser/resources/tab_search/app.ts
+++ b/chrome/browser/resources/tab_search/app.ts
@@ -534,8 +534,7 @@
   private tabData_(
       tab: Tab|RecentlyClosedTab, inActiveWindow: boolean, type: TabItemType,
       tabGroupsMap: Map<string, TabGroup>): TabData {
-    const tabData = new TabData(tab, type);
-    tabData.hostname = new URL(tab.url.url).hostname;
+    const tabData = new TabData(tab, type, new URL(tab.url.url).hostname);
 
     if (tab.groupId) {
       tabData.tabGroup = tabGroupsMap.get(tokenToString(tab.groupId));
diff --git a/chrome/browser/resources/tab_search/tab_data.ts b/chrome/browser/resources/tab_search/tab_data.ts
index b66f8d8..e4ce614 100644
--- a/chrome/browser/resources/tab_search/tab_data.ts
+++ b/chrome/browser/resources/tab_search/tab_data.ts
@@ -30,10 +30,11 @@
   tab: Tab|RecentlyClosedTab;
   hostname: string
 
-  constructor(tab: Tab|RecentlyClosedTab, type: TabItemType) {
+  constructor(tab: Tab|RecentlyClosedTab, type: TabItemType, hostname: string) {
     super();
     this.tab = tab;
     this.type = type;
+    this.hostname = hostname;
   }
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 0a40e204..3fe7e99d 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -881,7 +881,7 @@
     bool is_other_gaia_password) {
   DictionaryPrefUpdate unhandled_gaia_password_reuses(
       profile_->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
-  unhandled_gaia_password_reuses->Clear();
+  unhandled_gaia_password_reuses->DictClear();
   if (!is_other_gaia_password)
     MaybeLogPasswordCapture(/*did_log_in=*/true);
   for (auto& observer : observer_list_)
@@ -1580,7 +1580,7 @@
   DictionaryPrefUpdate unhandled_sync_password_reuses(
       profile_->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
   if (all_history) {
-    unhandled_sync_password_reuses->Clear();
+    unhandled_sync_password_reuses->DictClear();
     return;
   }
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
index 98aa854..40a1b69b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
@@ -182,12 +182,12 @@
     base::DictionaryValue* value_dict) {
   StateStoreData store_data;
   if (data.empty()) {
-    value_dict->Clear();
+    value_dict->DictClear();
     return PlatformStateStoreLoadResult::SUCCESS;
   }
   if (!store_data.ParseFromString(data))
     return PlatformStateStoreLoadResult::PARSE_ERROR;
-  value_dict->Clear();
+  value_dict->DictClear();
   RestoreFromProtobuf(store_data.type_to_incidents(), value_dict);
   return PlatformStateStoreLoadResult::SUCCESS;
 }
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index 5436f840..8357c63 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -89,7 +89,7 @@
 void StateStore::Transaction::ClearAll() {
   // Clear the preference if it exists and contains any values.
   if (store_->incidents_sent_ && !store_->incidents_sent_->DictEmpty())
-    GetPrefDict()->Clear();
+    GetPrefDict()->DictClear();
 }
 
 base::DictionaryValue* StateStore::Transaction::GetPrefDict() {
diff --git a/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc b/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
index 0b635a9..a23a4a7 100644
--- a/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
+++ b/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
@@ -53,7 +53,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
@@ -86,7 +86,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
@@ -131,7 +131,7 @@
 TEST(SmsRemoteFetcherTest, OneDeviceTimesOut) {
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
@@ -170,7 +170,7 @@
 TEST(SmsRemoteFetcherTest, RequestCancelled) {
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
@@ -219,7 +219,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   base::RunLoop loop;
@@ -245,7 +245,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   base::RunLoop loop;
@@ -271,7 +271,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
@@ -316,7 +316,7 @@
   content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
   TestingProfile profile;
-  content::WebContents::CreateParams create_params(&profile, nullptr);
+  content::WebContents::CreateParams create_params(&profile);
   auto web_contents = content::WebContents::Create(create_params);
 
   MockSharingService* service = CreateSharingService(&profile);
diff --git a/chrome/browser/signin/account_consistency_mode_manager.cc b/chrome/browser/signin/account_consistency_mode_manager.cc
index 0093a0e..6c1f0f0 100644
--- a/chrome/browser/signin/account_consistency_mode_manager.cc
+++ b/chrome/browser/signin/account_consistency_mode_manager.cc
@@ -92,12 +92,7 @@
   DCHECK(profile_);
   DCHECK(ShouldBuildServiceForProfile(profile));
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // Lacros doesn't support account inconsistency.
-  // TODO(crbug.com/1220066): Remove this section when Lacros stops building
-  // with DICE.
-  profile->GetPrefs()->SetBoolean(prefs::kSigninAllowed, true);
-#elif BUILDFLAG(ENABLE_DICE_SUPPORT)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
   PrefService* prefs = profile->GetPrefs();
   // Propagate settings changes from the previous launch to the signin-allowed
   // pref.
@@ -188,17 +183,10 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  bool is_account_manager_available = true;
   // Account consistency is unavailable on Managed Guest Sessions and Public
   // Sessions.
   if (profiles::IsPublicSession())
-    is_account_manager_available = false;
-
-  if (is_account_manager_available)
-    return AccountConsistencyMethod::kMirror;
-    // else: Fall through to ENABLE_DICE_SUPPORT section below.
-    // TODO(crbug.com/1198490): Return `AccountConsistencyMethod::kDisabled` if
-    // AccountManager is not available, when DICE has been disabled on Lacros.
+    return AccountConsistencyMethod::kDisabled;
 #endif
 
 #if BUILDFLAG(ENABLE_MIRROR)
diff --git a/chrome/browser/signin/account_consistency_mode_manager_unittest.cc b/chrome/browser/signin/account_consistency_mode_manager_unittest.cc
index 53912100..d0a4ee9 100644
--- a/chrome/browser/signin/account_consistency_mode_manager_unittest.cc
+++ b/chrome/browser/signin/account_consistency_mode_manager_unittest.cc
@@ -13,7 +13,6 @@
 #include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_notifier_impl.h"
@@ -44,7 +43,7 @@
       BuildTestingProfile(/*is_new_profile=*/false);
 
   signin::AccountConsistencyMethod method =
-#if BUILDFLAG(ENABLE_MIRROR) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_MIRROR)
       signin::AccountConsistencyMethod::kMirror;
 #elif BUILDFLAG(ENABLE_DICE_SUPPORT)
       signin::AccountConsistencyMethod::kDice;
@@ -62,9 +61,7 @@
       AccountConsistencyModeManager::IsDiceEnabledForProfile(profile.get()));
 }
 
-// TODO(crbug.com/1220066): Remove the lacros exclusion when DICE is disabled on
-// Lacros.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 // Checks that changing the signin-allowed pref changes the Dice state on next
 // startup.
 TEST(AccountConsistencyModeManagerTest, SigninAllowedChangesDiceState) {
@@ -202,9 +199,9 @@
         profile.get()));
   }
 }
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_MIRROR)
 // Mirror is enabled by default on Chrome OS, unless specified otherwise.
 TEST(AccountConsistencyModeManagerTest, MirrorEnabledByDefault) {
   // Creation of this object sets the current thread's id as UI thread.
@@ -258,19 +255,5 @@
   EXPECT_EQ(signin::AccountConsistencyMethod::kDisabled,
             AccountConsistencyModeManager::GetMethodForProfile(otr_profile));
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
-#if BUILDFLAG(ENABLE_MIRROR)
-// Test that Mirror is enabled for child accounts.
-TEST(AccountConsistencyModeManagerTest, MirrorChildAccount) {
-  content::BrowserTaskEnvironment task_environment;
-  TestingProfile profile;
-  profile.SetSupervisedUserId(supervised_users::kChildAccountSUID);
-  EXPECT_TRUE(
-      AccountConsistencyModeManager::IsMirrorEnabledForProfile(&profile));
-  EXPECT_FALSE(
-      AccountConsistencyModeManager::IsDiceEnabledForProfile(&profile));
-  EXPECT_EQ(signin::AccountConsistencyMethod::kMirror,
-            AccountConsistencyModeManager::GetMethodForProfile(&profile));
-}
 #endif  // BUILDFLAG(ENABLE_MIRROR)
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 00a7ce5a..b85dde4 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -207,14 +207,13 @@
 class ManageAccountsHeaderReceivedUserData
     : public base::SupportsUserData::Data {};
 
+#if BUILDFLAG(ENABLE_MIRROR)
 // Processes the mirror response header on the UI thread. Currently depending
 // on the value of |header_value|, it either shows the profile avatar menu, or
 // opens an incognito window/tab.
 void ProcessMirrorHeader(
     ManageAccountsParams manage_accounts_params,
     const content::WebContents::Getter& web_contents_getter) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) || \
-    defined(OS_ANDROID)
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   GAIAServiceType service_type = manage_accounts_params.service_type;
@@ -232,8 +231,6 @@
   AccountReconcilor* account_reconcilor =
       AccountReconcilorFactory::GetForProfile(profile);
   account_reconcilor->OnReceivedManageAccountsResponse(service_type);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) ||
-        // defined(OS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
@@ -392,6 +389,7 @@
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 }
+#endif  // BUILDFLAG(ENABLE_MIRROR)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
@@ -452,6 +450,7 @@
 }
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+#if BUILDFLAG(ENABLE_MIRROR)
 // Looks for the X-Chrome-Manage-Accounts response header, and if found,
 // tries to show the avatar bubble in the browser identified by the
 // child/route id. Must be called on IO thread.
@@ -502,6 +501,7 @@
       FROM_HERE, base::BindOnce(ProcessMirrorHeader, params,
                                 response->GetWebContentsGetter()));
 }
+#endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 void ProcessDiceResponseHeaderIfExists(ResponseAdapter* response,
@@ -635,6 +635,10 @@
   if (is_off_the_record)
     return;  // Account consistency is disabled in incognito.
 
+  // If new url is eligible to have the header, add it, otherwise remove it.
+
+// Mirror header:
+#if BUILDFLAG(ENABLE_MIRROR)
   int profile_mode_mask = PROFILE_MODE_DEFAULT;
   if (incognito_availibility ==
           static_cast<int>(IncognitoModePrefs::Availability::kDisabled) ||
@@ -650,10 +654,14 @@
   }
 #endif
 
-  // If new url is eligible to have the header, add it, otherwise remove it.
+  AppendOrRemoveMirrorRequestHeader(
+      request, redirect_url, gaia_id, is_child_account, account_consistency,
+      cookie_settings, profile_mode_mask, kChromeMirrorHeaderSource,
+      /*force_account_consistency=*/false);
+#endif  // BUILDFLAG(ENABLE_MIRROR)
 
+// Dice header:
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-  // Dice header:
   bool dice_header_added = AppendOrRemoveDiceRequestHeader(
       request, redirect_url, gaia_id, is_sync_enabled, account_consistency,
       cookie_settings, signin_scoped_device_id);
@@ -669,12 +677,6 @@
         &AccountReconcilorLockWrapper::DestroyAfterDelay, lock_wrapper));
   }
 #endif
-
-  // Mirror header:
-  AppendOrRemoveMirrorRequestHeader(
-      request, redirect_url, gaia_id, is_child_account, account_consistency,
-      cookie_settings, profile_mode_mask, kChromeMirrorHeaderSource,
-      /*force_account_consistency=*/false);
 }
 
 void ProcessAccountConsistencyResponseHeaders(ResponseAdapter* response,
@@ -683,10 +685,12 @@
   if (!gaia::IsGaiaSignonRealm(response->GetOrigin()))
     return;
 
+#if BUILDFLAG(ENABLE_MIRROR)
   // See if the response contains the X-Chrome-Manage-Accounts header. If so
   // show the profile avatar bubble so that user can complete signin/out
   // action the native UI.
   ProcessMirrorResponseHeaderIfExists(response, is_off_the_record);
+#endif
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Process the Dice header: on sign-in, exchange the authorization code for a
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc
index 59397971..43be4319f 100644
--- a/chrome/browser/signin/signin_ui_util_unittest.cc
+++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -71,9 +71,7 @@
   EXPECT_EQ("example-1.com", GetAllowedDomain("email@example-1.com"));
 }
 
-// TODO(https://crbug.com/1198523: Remove Lacros check once Dice is no longer
-// supported on Lacros.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 namespace {
 
diff --git a/chrome/browser/signin/signin_util_unittest.cc b/chrome/browser/signin/signin_util_unittest.cc
index ca18cbbb..8086257 100644
--- a/chrome/browser/signin/signin_util_unittest.cc
+++ b/chrome/browser/signin/signin_util_unittest.cc
@@ -7,6 +7,8 @@
 #include <memory>
 
 #include "base/feature_list.h"
+#include "build/buildflag.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/signin/signin_features.h"
@@ -43,6 +45,7 @@
   EXPECT_FALSE(signin_util::IsForceSigninEnabled());
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 class SigninUtilEnterpriseTest : public BrowserWithTestWindowTest {
  public:
   SigninUtilEnterpriseTest()
@@ -121,3 +124,4 @@
   EXPECT_TRUE(signin_util::ProfileSeparationEnforcedByPolicy(
       profile.get(), "primary_account_strict"));
 }
+#endif
diff --git a/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc b/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
index eff33c13..64da37b7 100644
--- a/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_pref_store_unittest.cc
@@ -136,7 +136,7 @@
 #endif
 
   // Activating the service again should not change anything.
-  fixture.changed_prefs()->Clear();
+  fixture.changed_prefs()->DictClear();
   service_.SetActive(true);
   EXPECT_EQ(0u, fixture.changed_prefs()->DictSize());
 
@@ -153,7 +153,7 @@
 
   // kForceGoogleSafeSearch and kForceYouTubeRestrict can be configured by the
   // custodian, overriding the hardcoded default.
-  fixture.changed_prefs()->Clear();
+  fixture.changed_prefs()->DictClear();
   service_.SetLocalSetting(supervised_users::kForceSafeSearch,
                            std::make_unique<base::Value>(false));
   EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
@@ -174,7 +174,7 @@
   histogram_tester.ExpectTotalCount(
       "SupervisedUsers.ExtensionsMayRequestPermissions", 0);
 
-  fixture.changed_prefs()->Clear();
+  fixture.changed_prefs()->DictClear();
   service_.SetLocalSetting(supervised_users::kGeolocationDisabled,
                            std::make_unique<base::Value>(false));
   EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
@@ -187,7 +187,7 @@
   histogram_tester.ExpectTotalCount(
       "SupervisedUsers.ExtensionsMayRequestPermissions", 1);
 
-  fixture.changed_prefs()->Clear();
+  fixture.changed_prefs()->DictClear();
   service_.SetLocalSetting(supervised_users::kGeolocationDisabled,
                            std::make_unique<base::Value>(true));
   EXPECT_EQ(1u, fixture.changed_prefs()->DictSize());
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index bb9b6d94..04904f7 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2043,6 +2043,8 @@
       "ash/accessibility/accessibility_controller_client.h",
       "ash/ambient/ambient_client_impl.cc",
       "ash/ambient/ambient_client_impl.h",
+      "ash/app_icon_color_cache.cc",
+      "ash/app_icon_color_cache.h",
       "ash/arc_custom_tab_modal_dialog_host.cc",
       "ash/arc_custom_tab_modal_dialog_host.h",
       "ash/ash_shell_init.cc",
@@ -2169,8 +2171,6 @@
       "ash/network/network_state_notifier.h",
       "ash/network/tether_notification_presenter.cc",
       "ash/network/tether_notification_presenter.h",
-      "ash/notification_badge_color_cache.cc",
-      "ash/notification_badge_color_cache.h",
       "ash/projector/pending_screencast_manager.cc",
       "ash/projector/pending_screencast_manager.h",
       "ash/projector/projector_app_client_impl.cc",
@@ -2873,8 +2873,6 @@
       "webui/signin/inline_login_handler_modal_delegate.h",
       "webui/signin/signin_helper_chromeos.cc",
       "webui/signin/signin_helper_chromeos.h",
-      "webui/version/version_handler_chromeos.cc",
-      "webui/version/version_handler_chromeos.h",
       "window_sizer/window_sizer_chromeos.cc",
       "window_sizer/window_sizer_chromeos.h",
     ]
@@ -3130,6 +3128,8 @@
       "views/tabs/tab_scrubber_chromeos.h",
       "webui/chromeos/chrome_url_disabled/chrome_url_disabled_ui.cc",
       "webui/chromeos/chrome_url_disabled/chrome_url_disabled_ui.h",
+      "webui/version/version_handler_chromeos.cc",
+      "webui/version/version_handler_chromeos.h",
     ]
     deps += [
       "//chrome/app:generated_resources",
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/res/values/dimens.xml b/chrome/browser/ui/android/quickactionsearchwidget/java/res/values/dimens.xml
index d095923d..994be297 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/res/values/dimens.xml
@@ -8,6 +8,12 @@
     <dimen name="quick_action_search_widget_background_radius">20dp</dimen>
     <dimen name="quick_action_search_widget_xsmall_background_radius">16dp</dimen>
 
+    <!-- Specify the reference widths of all widgets, guaranteeing that all of the
+    buttons will be visible and not truncated -->
+    <dimen name="quick_action_search_widget_medium_width">264dp</dimen>
+    <dimen name="quick_action_search_widget_small_width">312dp</dimen>
+    <dimen name="quick_action_search_widget_xsmall_width">312dp</dimen>
+
     <!-- Extra Small Widget Dimensions -->
     <dimen name="quick_action_search_widget_xsmall_height">48dp</dimen>
     <dimen name="quick_action_search_widget_xsmall_margin">8dp</dimen>
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegate.java b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegate.java
index de3cb8f..bcd8d7b 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegate.java
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegate.java
@@ -12,6 +12,7 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
+import androidx.annotation.DimenRes;
 import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -25,21 +26,230 @@
  * contains as much of the widget logic for the Quick Action Search Widget as possible.
  */
 public class QuickActionSearchWidgetProviderDelegate {
-    private final ComponentName mSearchActivityComponent;
-    private final Intent mStartIncognitoTabIntent;
-    private final Intent mStartDinoGameIntent;
+    /** Class describing widget variant characteristics. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static class WidgetVariant {
+        /** LayoutRes that describes this widget. */
+        public final @LayoutRes int layout;
+        /** The Reference width of the widget where all the content is visible. */
+        public final int widgetWidthDp;
+        /** The Reference height of the widget where all the content is visible. */
+        public final int widgetHeightDp;
+        /** The width of the button and surrounding margins. */
+        public final int buttonWidthDp;
+
+        /**
+         * @param context The application context.
+         * @param widthDimenRes The Resource ID describing reference width of the widget.
+         * @param heightDimenRes The Resource ID describing reference height of the widget.
+         * @param buttonWidthRes The Resource ID describing the width of the button.
+         * @param buttonMarginRes The Resource ID describing the width of the button margins.
+         */
+        public WidgetVariant(Context context, @LayoutRes int layoutRes, @DimenRes int widthDimenRes,
+                @DimenRes int heightDimenRes, @DimenRes int buttonWidthRes,
+                @DimenRes int buttonMarginRes) {
+            Resources res = context.getResources();
+            layout = layoutRes;
+            widgetWidthDp = getElementSizeInDP(res, widthDimenRes, 0);
+            widgetHeightDp = getElementSizeInDP(res, heightDimenRes, 0);
+            buttonWidthDp = getElementSizeInDP(res, buttonWidthRes, buttonMarginRes);
+        }
+
+        /**
+         * Helper method to return width of an element id DP.
+         *
+         * @param res Resources.
+         * @param mainDimenRes Core dimension resource id.
+         * @param marginDimenRes Margin dimension resource id (optional, may be 0).
+         * @return Element size measured in DP.
+         */
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        static int getElementSizeInDP(
+                Resources res, @DimenRes int mainDimenRes, @DimenRes int marginDimenRes) {
+            float density = res.getDisplayMetrics().density;
+            assert mainDimenRes != 0;
+
+            float size = res.getDimension(mainDimenRes);
+            if (marginDimenRes != 0) {
+                size += 2 * res.getDimension(marginDimenRes);
+            }
+            return (int) (size / density);
+        }
+
+        /**
+         * Given width of a target area compute how many buttons need to be hidden so that the
+         * widget fits in the area without truncation. When there is not enough width, we
+         * compensate by removing buttons.
+         *
+         * The method makes zero assumptions about the number of buttons, ie. it may return larger
+         * number than the total number of displayed buttons.
+         *
+         * @param areaWidthDp Width of the area where the widget is installed, expressed in dp.
+         * @return Number of buttons that have to be hidden so that this widget fits correctly in
+         *         the target area.
+         */
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        int computeNumberOfButtonsToHide(int areaWidthDp) {
+            // We compute the number of buttons to hide by subtracting the area width from
+            // the reference width (to check how much less space we have at our disposal), and
+            // dividing the remaining number by the button width, rounding up to the nearest
+            // integer. Negative values indicate we have "more space" than we need, hence no need to
+            // remove any buttons, so we just ignore this.
+            return (int) Math.max(
+                    0, Math.ceil(1.0 * (widgetWidthDp - areaWidthDp) / buttonWidthDp));
+        }
+    }
 
     /**
+     * Class describing the widget button offerings.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    static class WidgetButtonSettings {
+        /** Whether Voice Search button should be visible. */
+        public boolean voiceSearchVisible;
+        /** Whether Incognito mode button should be visible. */
+        public boolean incognitoModeVisible;
+        /** Whether Google Lens button should be visible. */
+        public boolean googleLensVisible;
+        /** Whether Dino Game button should be visible. */
+        public boolean dinoGameVisible;
+
+        /**
+         * Default constructor, accessible only for tests.
+         */
+        @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+        WidgetButtonSettings() {}
+
+        /**
+         * Construct an instance of this class from the SearchActivityPreferences.
+         */
+        public WidgetButtonSettings(SearchActivityPreferences prefs) {
+            voiceSearchVisible = prefs.voiceSearchAvailable;
+            incognitoModeVisible = prefs.incognitoAvailable;
+            googleLensVisible = prefs.googleLensAvailable;
+            dinoGameVisible = true;
+        }
+
+        /**
+         * Ensure that at least numButtonsToHide buttons are not visible.
+         *
+         * @param numButtonsToHide Number of buttons that should not be visible.
+         */
+        public void hideButtons(int numButtonsToHide) {
+            // Compute the actual number of visible buttons.
+            int numButtonsHidden = (voiceSearchVisible ? 0 : 1) + (incognitoModeVisible ? 0 : 1)
+                    + (googleLensVisible ? 0 : 1) + (dinoGameVisible ? 0 : 1);
+
+            // The series of calls below determine the priority in which we decide to hide buttons.
+            // In the event we run out of space, we hide Dino game first, then Lens and so on.
+            if (numButtonsToHide > numButtonsHidden && dinoGameVisible) {
+                dinoGameVisible = false;
+                numButtonsHidden++;
+            }
+
+            if (numButtonsToHide > numButtonsHidden && googleLensVisible) {
+                googleLensVisible = false;
+                numButtonsHidden++;
+            }
+
+            if (numButtonsToHide > numButtonsHidden && incognitoModeVisible) {
+                incognitoModeVisible = false;
+                numButtonsHidden++;
+            }
+
+            if (numButtonsToHide > numButtonsHidden && voiceSearchVisible) {
+                voiceSearchVisible = false;
+                numButtonsHidden++;
+            }
+        }
+    }
+
+    /** The target component to receive search actions. */
+    private final @NonNull ComponentName mSearchActivityComponent;
+    /** The intent to create a new incognito tab. */
+    private final @NonNull Intent mStartIncognitoTabIntent;
+    /** The intent to begin the Dino game. */
+    private final @NonNull Intent mStartDinoGameIntent;
+
+    /** Widget variant describing the Medium widget. */
+    private final @NonNull WidgetVariant mMediumWidgetVariant;
+    /** Widget variant describing the Small widget. */
+    private final @NonNull WidgetVariant mSmallWidgetVariant;
+    /** Widget variant describing the Extra Small widget. */
+    private final @NonNull WidgetVariant mExtraSmallWidgetVariant;
+
+    /**
+     * @param context Context that can be used to pre-compute values. Do not cache.
      * @param searchActivityComponent Component linking to SearchActivity where all Search related
      *         events will be propagated.
      * @param startIncognitoTabIntent A trusted intent starting a new Incognito tab.
      * @param startDinoGameIntent A trusted intent starting the Dino game.
      */
-    public QuickActionSearchWidgetProviderDelegate(@NonNull ComponentName searchActivityComponent,
-            @NonNull Intent startIncognitoTabIntent, @NonNull Intent startDinoGameIntent) {
+    public QuickActionSearchWidgetProviderDelegate(@NonNull Context context,
+            @NonNull ComponentName searchActivityComponent, @NonNull Intent startIncognitoTabIntent,
+            @NonNull Intent startDinoGameIntent) {
         mSearchActivityComponent = searchActivityComponent;
         mStartIncognitoTabIntent = startIncognitoTabIntent;
         mStartDinoGameIntent = startDinoGameIntent;
+
+        context = context.getApplicationContext();
+        mMediumWidgetVariant =
+                new WidgetVariant(context, R.layout.quick_action_search_widget_medium_layout,
+                        R.dimen.quick_action_search_widget_medium_width,
+                        R.dimen.quick_action_search_widget_medium_height,
+                        R.dimen.quick_action_search_widget_medium_button_width,
+                        R.dimen.quick_action_search_widget_medium_button_horizontal_margin);
+
+        mSmallWidgetVariant =
+                new WidgetVariant(context, R.layout.quick_action_search_widget_small_layout,
+                        R.dimen.quick_action_search_widget_small_width,
+                        R.dimen.quick_action_search_widget_small_height,
+                        R.dimen.quick_action_search_widget_small_button_width,
+                        R.dimen.quick_action_search_widget_small_button_horizontal_margin);
+
+        mExtraSmallWidgetVariant =
+                new WidgetVariant(context, R.layout.quick_action_search_widget_xsmall_layout,
+                        R.dimen.quick_action_search_widget_xsmall_width,
+                        R.dimen.quick_action_search_widget_xsmall_height,
+                        R.dimen.quick_action_search_widget_xsmall_button_width,
+                        R.dimen.quick_action_search_widget_xsmall_button_horizontal_margin);
+    }
+
+    /**
+     * Adjust button visibility to match feature availability and widget area.
+     *
+     * This method shows/hides widget buttons in order to reflect feature availability, eg.
+     * any feature that is not accessible to the user will have its corresponding button removed.
+     *
+     * The method evaluates widget area width to determine if any additional buttons have to be
+     * hidden in order to prevent button truncation: given widget variant and launcher area width
+     * decide how many buttons have to be hidden in order to prevent the truncation and hide any
+     * additional buttons in this order:
+     * - Dino Game (first),
+     * - Google Lens,
+     * - Incognito Mode,
+     * - Voice Search (last).
+     *
+     * @param views RemoteViews structure that hosts the buttons.
+     * @param prefs SearchActivityPreferences structure describing feature availability.
+     * @param variant Target widget variant.
+     * @param targetWidthDp The width of the space for the widget, as offered by the Launcher.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void applyRemoteViewsButtonVisibilityToFitWidth(@NonNull RemoteViews views,
+            @NonNull SearchActivityPreferences prefs, @NonNull WidgetVariant variant,
+            int targetWidthDp) {
+        WidgetButtonSettings settings = new WidgetButtonSettings(prefs);
+        settings.hideButtons(variant.computeNumberOfButtonsToHide(targetWidthDp));
+
+        views.setViewVisibility(R.id.voice_search_quick_action_button,
+                settings.voiceSearchVisible ? View.VISIBLE : View.GONE);
+        views.setViewVisibility(R.id.incognito_quick_action_button,
+                settings.incognitoModeVisible ? View.VISIBLE : View.GONE);
+        views.setViewVisibility(R.id.lens_quick_action_button,
+                settings.googleLensVisible ? View.VISIBLE : View.GONE);
+        views.setViewVisibility(
+                R.id.dino_quick_action_button, settings.dinoGameVisible ? View.VISIBLE : View.GONE);
     }
 
     /**
@@ -51,8 +261,7 @@
      */
     public @NonNull RemoteViews createDinoWidgetRemoteViews(
             @NonNull Context context, @NonNull SearchActivityPreferences prefs) {
-        return createWidgetRemoteViews(
-                context, prefs, R.layout.quick_action_search_widget_dino_layout);
+        return createWidgetRemoteViews(context, R.layout.quick_action_search_widget_dino_layout);
     }
 
     /**
@@ -72,11 +281,17 @@
     public @NonNull RemoteViews createSearchWidgetRemoteViews(@NonNull Context context,
             @NonNull SearchActivityPreferences prefs, int portraitModeWidthDp,
             int portraitModeHeightDp, int landscapeModeWidthDp, int landscapeModeHeightDp) {
-        return new RemoteViews(
-                createWidgetRemoteViews(context, prefs,
-                        getSearchWidgetLayoutForHeight(context, landscapeModeHeightDp)),
-                createWidgetRemoteViews(context, prefs,
-                        getSearchWidgetLayoutForHeight(context, portraitModeHeightDp)));
+        WidgetVariant landscapeVariant = getSearchWidgetVariantForHeight(landscapeModeHeightDp);
+        RemoteViews landscapeViews = createWidgetRemoteViews(context, landscapeVariant.layout);
+        applyRemoteViewsButtonVisibilityToFitWidth(
+                landscapeViews, prefs, landscapeVariant, landscapeModeWidthDp);
+
+        WidgetVariant portraitVariant = getSearchWidgetVariantForHeight(portraitModeHeightDp);
+        RemoteViews portraitViews = createWidgetRemoteViews(context, portraitVariant.layout);
+        applyRemoteViewsButtonVisibilityToFitWidth(
+                portraitViews, prefs, portraitVariant, portraitModeWidthDp);
+
+        return new RemoteViews(landscapeViews, portraitViews);
     }
 
     /**
@@ -87,21 +302,13 @@
      * @return Widget LayoutRes appropriate for the supplied height.
      */
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public @LayoutRes int getSearchWidgetLayoutForHeight(Context context, int heightDp) {
-        Resources res = context.getResources();
-        float density = res.getDisplayMetrics().density;
-
-        float smallWidgetMinHeightDp =
-                res.getDimension(R.dimen.quick_action_search_widget_small_height) / density;
-        float mediumWidgetMinHeightDp =
-                res.getDimension(R.dimen.quick_action_search_widget_medium_height) / density;
-
-        if (heightDp < smallWidgetMinHeightDp) {
-            return R.layout.quick_action_search_widget_xsmall_layout;
-        } else if (heightDp < mediumWidgetMinHeightDp) {
-            return R.layout.quick_action_search_widget_small_layout;
+    WidgetVariant getSearchWidgetVariantForHeight(int heightDp) {
+        if (heightDp < mSmallWidgetVariant.widgetHeightDp) {
+            return mExtraSmallWidgetVariant;
+        } else if (heightDp < mMediumWidgetVariant.widgetHeightDp) {
+            return mSmallWidgetVariant;
         }
-        return R.layout.quick_action_search_widget_medium_layout;
+        return mMediumWidgetVariant;
     }
 
     /**
@@ -110,12 +317,10 @@
      * widget.
      *
      * @param context The {@link Context} from which the widget is being updated.
-     * @param prefs Structure describing current preferences and feature availability.
      * @param layoutRes The Layout to inflate.
      * @return Widget RemoteViews structure describing layout and content of the widget.
      */
-    public RemoteViews createWidgetRemoteViews(@NonNull Context context,
-            @NonNull SearchActivityPreferences prefs, @LayoutRes int layoutRes) {
+    public RemoteViews createWidgetRemoteViews(@NonNull Context context, @LayoutRes int layoutRes) {
         RemoteViews remoteViews = new RemoteViews(context.getPackageName(), layoutRes);
 
         // Search Bar Intent
@@ -129,23 +334,17 @@
                 context, SearchActivityConstants.ACTION_START_EXTENDED_VOICE_SEARCH);
         remoteViews.setOnClickPendingIntent(
                 R.id.voice_search_quick_action_button, voiceSearchPendingIntent);
-        remoteViews.setViewVisibility(R.id.voice_search_quick_action_button,
-                prefs.voiceSearchAvailable ? View.VISIBLE : View.GONE);
 
         // Incognito Tab Intent
         PendingIntent incognitoTabPendingIntent =
                 createPendingIntent(context, mStartIncognitoTabIntent);
         remoteViews.setOnClickPendingIntent(
                 R.id.incognito_quick_action_button, incognitoTabPendingIntent);
-        remoteViews.setViewVisibility(R.id.incognito_quick_action_button,
-                prefs.incognitoAvailable ? View.VISIBLE : View.GONE);
 
         // Lens Search Intent
         PendingIntent lensSearchPendingIntent = createPendingIntentForAction(
                 context, SearchActivityConstants.ACTION_START_LENS_SEARCH);
         remoteViews.setOnClickPendingIntent(R.id.lens_quick_action_button, lensSearchPendingIntent);
-        remoteViews.setViewVisibility(R.id.lens_quick_action_button,
-                prefs.googleLensAvailable ? View.VISIBLE : View.GONE);
 
         // Dino Game intent
         PendingIntent dinoGamePendingIntent = createPendingIntent(context, mStartDinoGameIntent);
@@ -186,4 +385,22 @@
                 PendingIntent.FLAG_UPDATE_CURRENT
                         | IntentUtils.getPendingIntentMutabilityFlag(false));
     }
+
+    /** Returns the Medium widget variant for testing purposes. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    WidgetVariant getMediumWidgetVariantForTesting() {
+        return mMediumWidgetVariant;
+    }
+
+    /** Returns the Small widget variant for testing purposes. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    WidgetVariant getSmallWidgetVariantForTesting() {
+        return mSmallWidgetVariant;
+    }
+
+    /** Returns the Extra-small widget variant for testing purposes. */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    WidgetVariant getExtraSmallWidgetVariantForTesting() {
+        return mExtraSmallWidgetVariant;
+    }
 }
diff --git a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
index 27b726f..725eb13 100644
--- a/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
+++ b/chrome/browser/ui/android/quickactionsearchwidget/java/src/org/chromium/chrome/browser/ui/quickactionsearchwidget/QuickActionSearchWidgetProviderDelegateTest.java
@@ -27,16 +27,16 @@
 
 import org.chromium.base.IntentUtils;
 import org.chromium.base.test.BaseActivityTestRule;
-import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.searchwidget.SearchActivity;
+import org.chromium.chrome.browser.ui.quickactionsearchwidget.QuickActionSearchWidgetProviderDelegate.WidgetButtonSettings;
+import org.chromium.chrome.browser.ui.quickactionsearchwidget.QuickActionSearchWidgetProviderDelegate.WidgetVariant;
 import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityPreferencesManager.SearchActivityPreferences;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeApplicationTestUtils;
@@ -50,21 +50,14 @@
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
 @Features.EnableFeatures({ChromeFeatureList.QUICK_ACTION_SEARCH_WIDGET})
 public class QuickActionSearchWidgetProviderDelegateTest {
-    private static final class TestContext extends AdvancedMockContext {
-        public TestContext() {
-            super(InstrumentationRegistry.getInstrumentation()
-                            .getTargetContext()
-                            .getApplicationContext());
-        }
-    }
-
     @Rule
     public BaseActivityTestRule<Activity> mActivityTestRule =
             new BaseActivityTestRule<>(Activity.class);
 
     private View mWidgetView;
+    private View mDinoWidgetView;
     private QuickActionSearchWidgetProviderDelegate mDelegate;
-    private TestContext mContext;
+    private Context mContext;
     private int mDefaultWidgetWidthDp;
     private int mXSmallWidgetMinHeightDp;
     private int mSmallWidgetMinHeightDp;
@@ -73,12 +66,13 @@
     @Before
     public void setUp() {
         ChromeApplicationTestUtils.setUp(InstrumentationRegistry.getTargetContext());
-
-        mContext = new TestContext();
+        mContext = InstrumentationRegistry.getInstrumentation()
+                           .getTargetContext()
+                           .getApplicationContext();
 
         ComponentName searchActivityComponent = new ComponentName(mContext, SearchActivity.class);
 
-        mDelegate = new QuickActionSearchWidgetProviderDelegate(searchActivityComponent,
+        mDelegate = new QuickActionSearchWidgetProviderDelegate(mContext, searchActivityComponent,
                 IntentHandler.createTrustedOpenNewTabIntent(mContext, /*incognito=*/true),
                 createDinoIntent(mContext));
 
@@ -141,7 +135,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1225949")
     public void testDinoButtonClick() throws Exception {
         QuickActionSearchWidgetTestUtils.assertDinoGameLaunchedAfterAction(
                 mActivityTestRule, () -> {
@@ -151,6 +144,17 @@
         ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
     }
 
+    @Test
+    @SmallTest
+    public void testDinoWidgetDinoButtonClick() throws Exception {
+        QuickActionSearchWidgetTestUtils.assertDinoGameLaunchedAfterAction(
+                mActivityTestRule, () -> {
+                    QuickActionSearchWidgetTestUtils.clickOnView(
+                            mDinoWidgetView, R.id.dino_quick_action_button);
+                });
+        ApplicationTestUtils.finishActivity(mActivityTestRule.getActivity());
+    }
+
     private void setUpViews() {
         FrameLayout parentView = new FrameLayout(mContext);
 
@@ -169,6 +173,8 @@
                                       // Landscape mode dimensions:
                                       mDefaultWidgetWidthDp, mMediumWidgetMinHeightDp)
                               .apply(mContext, null);
+        mDinoWidgetView =
+                mDelegate.createDinoWidgetRemoteViews(mContext, prefs).apply(mContext, null);
     }
 
     /**
@@ -208,7 +214,7 @@
      */
     @Test
     @SmallTest
-    public void doVerticalWidgetResizeOfSmallWidget() {
+    public void testWidgetVerticalResizing() {
         // Validate all height pairs (exhausts the solution space).
         // This includes both reasonable and unreasonable pairs, ie. the assertion that the
         // MIN_HEIGHT <= MAX_HEIGHT does not have to hold true.
@@ -243,10 +249,213 @@
         };
 
         for (VerticalResizeHeightVariant variant : variants) {
-            Integer layoutRes =
-                    mDelegate.getSearchWidgetLayoutForHeight(mContext, variant.heightDp);
+            Integer layoutRes = mDelegate.getSearchWidgetVariantForHeight(variant.heightDp).layout;
             Assert.assertEquals("Unexpected layout where height=" + variant.variantName,
                     variant.layoutRes, layoutRes);
         }
     }
+
+    @Test
+    @SmallTest
+    public void computeNumberOfButtonsToHide_mediumWidget() {
+        testComputeNumberOfButtonsToHideForVariant(mDelegate.getMediumWidgetVariantForTesting());
+    }
+
+    @Test
+    @SmallTest
+    public void computeNumberOfButtonsToHide_smallWidget() {
+        testComputeNumberOfButtonsToHideForVariant(mDelegate.getSmallWidgetVariantForTesting());
+    }
+
+    @Test
+    @SmallTest
+    public void computeNumberOfButtonsToHide_extraSmallWidget() {
+        testComputeNumberOfButtonsToHideForVariant(
+                mDelegate.getExtraSmallWidgetVariantForTesting());
+    }
+
+    private void testComputeNumberOfButtonsToHideForVariant(WidgetVariant variant) {
+        // Target area width >= reference width: widget fits fully, no buttons should be hidden.
+        Assert.assertEquals(0, variant.computeNumberOfButtonsToHide(variant.widgetWidthDp * 2));
+        Assert.assertEquals(0, variant.computeNumberOfButtonsToHide(variant.widgetWidthDp + 1));
+        Assert.assertEquals(0, variant.computeNumberOfButtonsToHide(variant.widgetWidthDp));
+
+        // Target area width < reference width:
+        Assert.assertEquals(1, variant.computeNumberOfButtonsToHide(variant.widgetWidthDp - 1));
+        Assert.assertEquals(1,
+                variant.computeNumberOfButtonsToHide(
+                        variant.widgetWidthDp - variant.buttonWidthDp));
+        Assert.assertEquals(2,
+                variant.computeNumberOfButtonsToHide(
+                        variant.widgetWidthDp - variant.buttonWidthDp - 1));
+        Assert.assertEquals(3,
+                variant.computeNumberOfButtonsToHide(
+                        variant.widgetWidthDp - variant.buttonWidthDp * 3));
+    }
+
+    @Test
+    @SmallTest
+    public void getElementSizeInDP_noMargins() {
+        Resources res = mContext.getResources();
+
+        // Convert a simple dimension into DP.
+        float expectedSizeDp =
+                res.getDimension(R.dimen.quick_action_search_widget_medium_button_width)
+                / res.getDisplayMetrics().density;
+
+        // Check that the method returns that same value after conversion.
+        Assert.assertEquals((int) expectedSizeDp,
+                WidgetVariant.getElementSizeInDP(
+                        res, R.dimen.quick_action_search_widget_medium_button_width, 0));
+    }
+
+    @Test
+    @SmallTest
+    public void getElementSizeInDP_withMargins() {
+        Resources res = mContext.getResources();
+
+        // Convert a single dimension + surrounding margins into DP.
+        float expectedSizeDp =
+                (res.getDimension(R.dimen.quick_action_search_widget_medium_button_width)
+                        + res.getDimension(
+                                  R.dimen.quick_action_search_widget_medium_button_horizontal_margin)
+                                * 2)
+                / res.getDisplayMetrics().density;
+
+        // Check that the method returns that same value after conversion.
+        Assert.assertEquals((int) expectedSizeDp,
+                WidgetVariant.getElementSizeInDP(res,
+                        R.dimen.quick_action_search_widget_medium_button_width,
+                        R.dimen.quick_action_search_widget_medium_button_horizontal_margin));
+    }
+
+    @Test
+    @SmallTest
+    public void widgetButtonSettings_hide0Buttons() {
+        // In the event the code requests K buttons to be hidden, but at least K buttons are already
+        // hidden, the code is expected to take no additional action.
+        WidgetButtonSettings settings = new WidgetButtonSettings();
+
+        // Verify that the call to hideButtons does not reveal buttons.
+        settings.hideButtons(0);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertFalse(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+
+        // Retry with all buttons visible.
+        settings.voiceSearchVisible = true;
+        settings.incognitoModeVisible = true;
+        settings.googleLensVisible = true;
+        settings.dinoGameVisible = true;
+
+        // Verify that the call to hideButtons does not hide anything if not necessary.
+        settings.hideButtons(0);
+        Assert.assertTrue(settings.voiceSearchVisible);
+        Assert.assertTrue(settings.incognitoModeVisible);
+        Assert.assertTrue(settings.googleLensVisible);
+        Assert.assertTrue(settings.dinoGameVisible);
+    }
+
+    @Test
+    @SmallTest
+    public void widgetButtonSettings_hide1Button() {
+        // In the event the code requests K buttons to be hidden, but at least K buttons are already
+        // hidden, the code is expected to take no additional action.
+        WidgetButtonSettings settings = new WidgetButtonSettings();
+
+        // Mark one of the buttons as unavailable. In theory it shouldn't matter which one we pick.
+        settings.voiceSearchVisible = false;
+        settings.incognitoModeVisible = true;
+        settings.googleLensVisible = true;
+        settings.dinoGameVisible = true;
+
+        // Tell settings we need 1 button removed.
+        settings.hideButtons(1);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertTrue(settings.incognitoModeVisible);
+        Assert.assertTrue(settings.googleLensVisible);
+        Assert.assertTrue(settings.dinoGameVisible);
+
+        // Mark the VoiceSearch button as available and try again.
+        // The logic should hide the dino game first.
+        settings.voiceSearchVisible = true;
+        settings.hideButtons(1);
+        Assert.assertTrue(settings.voiceSearchVisible);
+        Assert.assertTrue(settings.incognitoModeVisible);
+        Assert.assertTrue(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+    }
+
+    @Test
+    @SmallTest
+    public void widgetButtonSettings_hide3Button() {
+        // In the event the code requests K buttons to be hidden, but at least K buttons are already
+        // hidden, the code is expected to take no additional action.
+        WidgetButtonSettings settings = new WidgetButtonSettings();
+
+        // Mark one of the buttons as unavailable. In theory it shouldn't matter which one we pick.
+        settings.voiceSearchVisible = false;
+        settings.incognitoModeVisible = false;
+        settings.googleLensVisible = false;
+        settings.dinoGameVisible = true;
+
+        // Tell settings we need 3 buttons removed.
+        settings.hideButtons(3);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertFalse(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertTrue(settings.dinoGameVisible);
+
+        // Mark the Incognito mode and Lens buttons as available and try again.
+        // The logic should hide the Dino game and Google Lens.
+        settings.incognitoModeVisible = true;
+        settings.googleLensVisible = true;
+        settings.hideButtons(3);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertTrue(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+
+        // Finally, tell the logic that voice search is available and see that it removes the
+        // Incognito mode.
+        settings.voiceSearchVisible = true;
+        settings.hideButtons(3);
+        Assert.assertTrue(settings.voiceSearchVisible);
+        Assert.assertFalse(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+    }
+
+    @Test
+    @SmallTest
+    public void widgetButtonSettings_hideLotsOfButtons() {
+        // In the event the code requests K buttons to be hidden, but at least K buttons are already
+        // hidden, the code is expected to take no additional action.
+        WidgetButtonSettings settings = new WidgetButtonSettings();
+
+        // Mark one of the buttons as unavailable. In theory it shouldn't matter which one we pick.
+        settings.voiceSearchVisible = true;
+        settings.incognitoModeVisible = true;
+        settings.googleLensVisible = true;
+        settings.dinoGameVisible = true;
+
+        // Tell settings we need 4 or more buttons.
+        settings.hideButtons(4);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertFalse(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+
+        // Try again with more buttons.
+        settings.voiceSearchVisible = true;
+        settings.incognitoModeVisible = true;
+        settings.googleLensVisible = true;
+        settings.dinoGameVisible = true;
+        settings.hideButtons(40);
+        Assert.assertFalse(settings.voiceSearchVisible);
+        Assert.assertFalse(settings.incognitoModeVisible);
+        Assert.assertFalse(settings.googleLensVisible);
+        Assert.assertFalse(settings.dinoGameVisible);
+    }
 }
diff --git a/chrome/browser/ui/app_list/app_list_model_updater.h b/chrome/browser/ui/app_list/app_list_model_updater.h
index bb3f90a..7402a52 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -76,6 +76,7 @@
   virtual void SetItemPosition(const std::string& id,
                                const syncer::StringOrdinal& new_position) {}
   virtual void SetItemIsPersistent(const std::string& id, bool is_persistent) {}
+  virtual void SetIsNewInstall(const std::string& id, bool is_new_install) {}
   virtual void SetItemFolderId(const std::string& id,
                                const std::string& folder_id) = 0;
   virtual void SetNotificationBadgeColor(const std::string& id,
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 028cc9e..c782a91 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -1153,7 +1153,7 @@
   // Reset local state and recreate from sync info.
   DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
                                    prefs::kAppListLocalState);
-  pref_update->Clear();
+  pref_update->DictClear();
 
   sync_processor_ = std::move(sync_processor);
   sync_error_handler_ = std::move(error_handler);
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 56e074b..9121f74 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/app_list/app_service/app_service_app_item.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
@@ -12,8 +13,8 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/check.h"
-#include "base/compiler_specific.h"
 #include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/notreached.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -26,6 +27,40 @@
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/mojom/types.mojom-shared.h"
+
+namespace {
+
+// Returns true if `app_update` should be considered a new app install.
+bool IsNewInstall(const apps::AppUpdate& app_update) {
+  // Ignore internally-installed apps.
+  if (app_update.InstalledInternally() == apps::mojom::OptionalBool::kTrue)
+    return false;
+
+  switch (app_update.AppType()) {
+    case apps::mojom::AppType::kUnknown:
+    case apps::mojom::AppType::kBuiltIn:
+    case apps::mojom::AppType::kStandaloneBrowser:
+    case apps::mojom::AppType::kSystemWeb:
+      // Chrome, Lacros, Settings, etc. are built-in.
+      return false;
+    case apps::mojom::AppType::kMacOs:
+      NOTREACHED();
+      return false;
+    case apps::mojom::AppType::kArc:
+    case apps::mojom::AppType::kCrostini:
+    case apps::mojom::AppType::kChromeApp:
+    case apps::mojom::AppType::kWeb:
+    case apps::mojom::AppType::kPluginVm:
+    case apps::mojom::AppType::kRemote:
+    case apps::mojom::AppType::kBorealis:
+    case apps::mojom::AppType::kStandaloneBrowserChromeApp:
+      // Other app types are user-installed.
+      return true;
+  }
+}
+
+}  // namespace
 
 // static
 const char AppServiceAppItem::kItemType[] = "AppServiceAppItem";
@@ -36,9 +71,8 @@
     const app_list::AppListSyncableService::SyncItem* sync_item,
     const apps::AppUpdate& app_update)
     : ChromeAppListItem(profile, app_update.AppId()),
-      app_type_(app_update.AppType()),
-      is_platform_app_(false) {
-  OnAppUpdate(app_update, true);
+      app_type_(app_update.AppType()) {
+  OnAppUpdate(app_update, /*in_constructor=*/true);
   if (sync_item && sync_item->item_ordinal.IsValid()) {
     InitFromSync(sync_item);
   } else {
@@ -69,6 +103,13 @@
     }
   }
 
+  if (ash::features::IsProductivityLauncherEnabled()) {
+    const bool is_new_install = !sync_item && IsNewInstall(app_update);
+    DVLOG(1) << "New AppServiceAppItem is_new_install " << is_new_install
+             << " from update " << app_update;
+    SetIsNewInstall(is_new_install);
+  }
+
   // Set model updater last to avoid being called during construction.
   set_model_updater(model_updater);
 }
@@ -76,7 +117,7 @@
 AppServiceAppItem::~AppServiceAppItem() = default;
 
 void AppServiceAppItem::OnAppUpdate(const apps::AppUpdate& app_update) {
-  OnAppUpdate(app_update, false);
+  OnAppUpdate(app_update, /*in_constructor=*/false);
 }
 
 void AppServiceAppItem::OnAppUpdate(const apps::AppUpdate& app_update,
@@ -177,6 +218,10 @@
 
 void AppServiceAppItem::Launch(int event_flags,
                                apps::mojom::LaunchSource launch_source) {
+  if (ash::features::IsProductivityLauncherEnabled()) {
+    // Launching an app clears the "new install" badge.
+    SetIsNewInstall(false);
+  }
   apps::AppServiceProxyFactory::GetForProfile(profile())->Launch(
       id(), event_flags, launch_source,
       apps::MakeWindowInfo(GetController()->GetAppListDisplayId()));
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.h b/chrome/browser/ui/app_list/app_service/app_service_app_item.h
index e09fceb3..b8c28c04 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.h
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.h
@@ -14,6 +14,7 @@
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/mojom/types.mojom-forward.h"
 
+// An app list item provided by the app service.
 class AppServiceAppItem : public ChromeAppListItem,
                           public app_list::AppContextMenuDelegate {
  public:
@@ -23,10 +24,8 @@
                     AppListModelUpdater* model_updater,
                     const app_list::AppListSyncableService::SyncItem* sync_item,
                     const apps::AppUpdate& app_update);
-
   AppServiceAppItem(const AppServiceAppItem&) = delete;
   AppServiceAppItem& operator=(const AppServiceAppItem&) = delete;
-
   ~AppServiceAppItem() override;
 
   void OnAppUpdate(const apps::AppUpdate& app_update);
@@ -50,8 +49,8 @@
   void CallLoadIcon(bool allow_placeholder_icon);
   void OnLoadIcon(apps::IconValuePtr icon_value);
 
-  apps::mojom::AppType app_type_;
-  bool is_platform_app_;
+  const apps::mojom::AppType app_type_;
+  bool is_platform_app_ = false;
 
   std::unique_ptr<app_list::AppContextMenu> context_menu_;
 
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.cc b/chrome/browser/ui/app_list/chrome_app_list_item.cc
index 48edc45..69e58bbc 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_model_updater.h"
-#include "chrome/browser/ui/ash/notification_badge_color_cache.h"
+#include "chrome/browser/ui/ash/app_icon_color_cache.h"
 #include "extensions/browser/app_sorting.h"
 #include "extensions/browser/extension_system.h"
 #include "ui/gfx/color_utils.h"
@@ -171,8 +171,8 @@
   metadata_->icon = icon;
   metadata_->icon.EnsureRepsForSupportedScales();
   metadata_->badge_color =
-      ash::NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(id(),
-                                                                          icon);
+      ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(id(),
+                                                                       icon);
 
   // TODO(https://crbug.com/1270898): update `metadata_->icon_color` when icon
   // is set.
@@ -214,6 +214,13 @@
   metadata_->is_page_break = is_page_break;
 }
 
+void ChromeAppListItem::SetIsNewInstall(bool is_new_install) {
+  metadata_->is_new_install = is_new_install;
+  AppListModelUpdater* updater = model_updater();
+  if (updater)
+    updater->SetIsNewInstall(id(), is_new_install);
+}
+
 void ChromeAppListItem::SetChromeFolderId(const std::string& folder_id) {
   metadata_->folder_id = folder_id;
 }
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.h b/chrome/browser/ui/app_list/chrome_app_list_item.h
index 861b478..e028852 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -82,6 +82,7 @@
   void SetFolderId(const std::string& folder_id);
   void SetIsPageBreak(bool is_page_break);
   void SetIsPersistent(bool is_persistent);
+  void SetIsNewInstall(bool is_new_install);
 
   // The following methods won't make changes to Ash and it should be called
   // by this item itself or the model updater.
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index e5962f79..32bbe60 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -399,6 +399,13 @@
   model_.SetItemMetadata(id, std::move(data));
 }
 
+void ChromeAppListModelUpdater::SetIsNewInstall(const std::string& id,
+                                                bool is_new_install) {
+  ash::AppListItem* item = model_.FindItem(id);
+  if (item)
+    item->SetIsNewInstall(is_new_install);  // Notifies observers.
+}
+
 void ChromeAppListModelUpdater::SetItemFolderId(const std::string& id,
                                                 const std::string& folder_id) {
   ash::AppListItem* item = model_.FindItem(id);
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index ba9cc2c..6243a74 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -65,6 +65,7 @@
   void SetItemPosition(const std::string& id,
                        const syncer::StringOrdinal& new_position) override;
   void SetItemIsPersistent(const std::string& id, bool is_persistent) override;
+  void SetIsNewInstall(const std::string& id, bool is_new_install) override;
   void SetItemFolderId(const std::string& id,
                        const std::string& folder_id) override;
   void SetNotificationBadgeColor(const std::string& id,
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc
index 54b4ef3f..9ba1c80 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater_browsertest.cc
@@ -4,11 +4,13 @@
 
 #include "ash/app_list/model/app_list_item.h"
 #include "ash/app_list/model/app_list_model.h"
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/accelerators.h"
 #include "ash/public/cpp/test/app_list_test_api.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
@@ -38,9 +40,7 @@
 
 class OemAppPositionTest : public ash::LoginManagerTest {
  public:
-  OemAppPositionTest() : LoginManagerTest() {
-    login_mixin_.AppendRegularUsers(1);
-  }
+  OemAppPositionTest() { login_mixin_.AppendRegularUsers(1); }
   OemAppPositionTest(const OemAppPositionTest&) = delete;
   OemAppPositionTest& operator=(const OemAppPositionTest&) = delete;
   ~OemAppPositionTest() override = default;
@@ -73,13 +73,14 @@
   ash::LoginManagerMixin login_mixin_{&mixin_host_};
 };
 
-class AppPositionReorderingTest : public extensions::ExtensionBrowserTest {
+class ChromeAppListModelUpdaterTest : public extensions::ExtensionBrowserTest {
  public:
-  AppPositionReorderingTest() = default;
-  ~AppPositionReorderingTest() override = default;
-  AppPositionReorderingTest(const AppPositionReorderingTest& other) = delete;
-  AppPositionReorderingTest& operator=(const AppPositionReorderingTest& other) =
+  ChromeAppListModelUpdaterTest() = default;
+  ~ChromeAppListModelUpdaterTest() override = default;
+  ChromeAppListModelUpdaterTest(const ChromeAppListModelUpdaterTest& other) =
       delete;
+  ChromeAppListModelUpdaterTest& operator=(
+      const ChromeAppListModelUpdaterTest& other) = delete;
 
  protected:
   void SetUpOnMainThread() override {
@@ -94,6 +95,16 @@
 
   ash::AppListTestApi app_list_test_api_;
 };
+
+using AppPositionReorderingTest = ChromeAppListModelUpdaterTest;
+
+class ChromeAppListModelUpdaterProductivityLauncherTest
+    : public ChromeAppListModelUpdaterTest {
+ private:
+  base::test::ScopedFeatureList feature_list_{
+      ash::features::kProductivityLauncher};
+};
+
 // Tests that an Oem app and its folder are created with valid positions after
 // sign-in.
 IN_PROC_BROWSER_TEST_F(OemAppPositionTest, ValidOemAppPosition) {
@@ -379,3 +390,43 @@
   EXPECT_EQ(std::vector<std::string>({app1_id, app3_id, app2_id}),
             trailing_items);
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeAppListModelUpdaterProductivityLauncherTest,
+                       IsNewInstall) {
+  AppListClientImpl* client = AppListClientImpl::GetInstance();
+  ASSERT_TRUE(client);
+  AppListModelUpdater* model_updater = test::GetModelUpdater(client);
+  ASSERT_TRUE(model_updater);
+
+  // The built-in "Web Store" app is not a new install.
+  ChromeAppListItem* web_store_item =
+      model_updater->FindItem(extensions::kWebStoreAppId);
+  ASSERT_TRUE(web_store_item);
+  EXPECT_FALSE(web_store_item->CloneMetadata()->is_new_install);
+
+  // Install 2 apps.
+  const std::string app1_id =
+      LoadExtension(test_data_dir_.AppendASCII("app1"))->id();
+  ASSERT_FALSE(app1_id.empty());
+  const std::string app2_id =
+      LoadExtension(test_data_dir_.AppendASCII("app2"))->id();
+  ASSERT_FALSE(app2_id.empty());
+
+  // Both apps are new installs.
+  ChromeAppListItem* item1 = model_updater->FindItem(app1_id);
+  ASSERT_TRUE(item1);
+  EXPECT_TRUE(item1->CloneMetadata()->is_new_install);
+
+  ChromeAppListItem* item2 = model_updater->FindItem(app2_id);
+  ASSERT_TRUE(item2);
+  EXPECT_TRUE(item2->CloneMetadata()->is_new_install);
+
+  // Launch the first app.
+  item1->Activate(ui::EF_NONE);
+
+  // First app is no longer a new install.
+  EXPECT_FALSE(item1->CloneMetadata()->is_new_install);
+
+  // Second app is still a new install.
+  EXPECT_TRUE(item2->CloneMetadata()->is_new_install);
+}
diff --git a/chrome/browser/ui/app_list/reorder/app_list_reorder_util.cc b/chrome/browser/ui/app_list/reorder/app_list_reorder_util.cc
index f171f80..9d7b613 100644
--- a/chrome/browser/ui/app_list/reorder/app_list_reorder_util.cc
+++ b/chrome/browser/ui/app_list/reorder/app_list_reorder_util.cc
@@ -5,12 +5,30 @@
 #include "chrome/browser/ui/app_list/reorder/app_list_reorder_util.h"
 
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
+#include "chrome/browser/ui/ash/app_icon_color_cache.h"
+#include "ui/gfx/color_analysis.h"
 
 namespace app_list {
 namespace reorder {
 
 const float kOrderResetThreshold = 0.2f;
 
+// On the 360 degree hue color spectrum, this value is used as a cutuff to
+// indicate that any value equal to or higher than this is considered red.
+const float kRedHueCutoff = 315.0f;
+
+// An hsv color with saturation below this cutoff will be categorized as either
+// black or white.
+const float kBlackWhiteSaturationCutoff = 0.1f;
+
+// When an hsv color has a saturation below 'kBlackWhiteSaturationCutoff' then
+// if its value is below this cutoff it will be categorized as white and with a
+// value above this cutoff is will be categorized as black.
+const float kBlackWhiteLowSaturatonValueCutoff = 0.9f;
+
+// An hsv color with a value less than this cutoff will be categorized as black.
+const float kBlackValueCutoff = 0.35f;
+
 // ReorderParam ----------------------------------------------------------------
 
 ReorderParam::ReorderParam(const std::string& new_sync_item_id,
@@ -77,5 +95,188 @@
   return wrappers;
 }
 
+// Color Sort Utilities -------------------------------------------------------
+
+sync_pb::AppListSpecifics::ColorGroup CalculateBackgroundColorGroup(
+    const SkBitmap& source,
+    sync_pb::AppListSpecifics::ColorGroup light_vibrant_group) {
+  if (source.empty()) {
+    return sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_WHITE;
+  }
+
+  DCHECK_EQ(kN32_SkColorType, source.info().colorType());
+
+  int width = source.width();
+  int height = source.height();
+
+  sync_pb::AppListSpecifics::ColorGroup left_group = sync_pb::AppListSpecifics::
+      ColorGroup::AppListSpecifics_ColorGroup_COLOR_BLACK;
+  sync_pb::AppListSpecifics::ColorGroup right_group = sync_pb::
+      AppListSpecifics::ColorGroup::AppListSpecifics_ColorGroup_COLOR_BLACK;
+
+  // Find the color group for the first opaque pixel on the left edge of the
+  // icon.
+  const SkColor* current =
+      reinterpret_cast<SkColor*>(source.getAddr32(0, height / 2));
+  for (int x = 0; x < width; ++x, ++current) {
+    if (SkColorGetA(*current) < SK_AlphaOPAQUE) {
+      continue;
+    } else {
+      left_group = ColorToColorGroup(*current);
+      break;
+    }
+  }
+
+  // Find the color group for the first opaque pixel on the right edge of the
+  // icon.
+  current = reinterpret_cast<SkColor*>(source.getAddr32(width - 1, height / 2));
+  for (int x = width - 1; x >= 0; --x, --current) {
+    if (SkColorGetA(*current) < SK_AlphaOPAQUE) {
+      continue;
+    } else {
+      right_group = ColorToColorGroup(*current);
+      break;
+    }
+  }
+
+  // If the left and right edge have the same color grouping, then return that
+  // group as the calculated background color group.
+  if (left_group == right_group)
+    return left_group;
+
+  // Find the color group for the first opaque pixel on the top edge of the
+  // icon.
+  sync_pb::AppListSpecifics::ColorGroup top_group = sync_pb::AppListSpecifics::
+      ColorGroup::AppListSpecifics_ColorGroup_COLOR_BLACK;
+  current = reinterpret_cast<SkColor*>(source.getAddr32(width / 2, 0));
+  const int row_bytes = source.rowBytes();
+  for (int y = 0; y < height; ++y, current += row_bytes) {
+    if (SkColorGetA(*current) < SK_AlphaOPAQUE) {
+      continue;
+    } else {
+      top_group = ColorToColorGroup(*current);
+      break;
+    }
+  }
+
+  // If the top edge has a matching color group with the left or right group,
+  // then return that group.
+  if (top_group == right_group || top_group == left_group)
+    return top_group;
+
+  // When all three sampled color groups are different, then there is no
+  // conclusive color group for the icon's background. Return the group
+  // corresponding to the app icon's light vibrant color.
+  return light_vibrant_group;
+}
+
+sync_pb::AppListSpecifics::ColorGroup ColorToColorGroup(SkColor color) {
+  SkScalar hsv[3];
+  SkColorToHSV(color, hsv);
+
+  const float h = hsv[0];
+  const float s = hsv[1];
+  const float v = hsv[2];
+
+  sync_pb::AppListSpecifics::ColorGroup group;
+
+  // Assign the ColorGroup based on the hue of `color`. Each if statement checks
+  // the value of the hue and groups the color based on it. These values are
+  // approximations for grouping like colors together.
+  if (h < 15) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_RED;
+  } else if (h < 45) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_ORANGE;
+  } else if (h < 75) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_YELLOW;
+  } else if (h < 182) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_GREEN;
+  } else if (h < 255) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_BLUE;
+  } else if (h < kRedHueCutoff) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_MAGENTA;
+  } else {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_RED;
+  }
+
+  if (s < kBlackWhiteSaturationCutoff) {
+    if (v < kBlackWhiteLowSaturatonValueCutoff) {
+      group = sync_pb::AppListSpecifics::ColorGroup::
+          AppListSpecifics_ColorGroup_COLOR_BLACK;
+    } else {
+      group = sync_pb::AppListSpecifics::ColorGroup::
+          AppListSpecifics_ColorGroup_COLOR_WHITE;
+    }
+  } else if (v < kBlackValueCutoff) {
+    group = sync_pb::AppListSpecifics::ColorGroup::
+        AppListSpecifics_ColorGroup_COLOR_BLACK;
+  }
+
+  return group;
+}
+
+ash::IconColor GetSortableIconColorForApp(const std::string& id,
+                                          const gfx::ImageSkia& image) {
+  SkColor extracted_light_vibrant_color =
+      ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(id,
+                                                                       image);
+
+  sync_pb::AppListSpecifics::ColorGroup light_vibrant_color_group =
+      ColorToColorGroup(extracted_light_vibrant_color);
+
+  ash::IconColor sortable_icon_color;
+  sync_pb::AppListSpecifics::ColorGroup background_color_group =
+      CalculateBackgroundColorGroup(*image.bitmap(), light_vibrant_color_group);
+
+  // `hue` represents the hue of the extracted light vibrant color and can be
+  // defined by the interval [-1, 360], where -1 (kHueMin) denotes that the hue
+  // should come before all other hue values, and 360 (kHueMax) denotes that the
+  // hue should come after all other hue values.
+  int hue;
+
+  if (light_vibrant_color_group ==
+      sync_pb::AppListSpecifics::ColorGroup::
+          AppListSpecifics_ColorGroup_COLOR_BLACK) {
+    // If `light_vibrant_color_group` is black it should be ordered after all
+    // other hues.
+    hue = ash::IconColor::kHueMax;
+  } else if (light_vibrant_color_group ==
+             sync_pb::AppListSpecifics::ColorGroup::
+                 AppListSpecifics_ColorGroup_COLOR_WHITE) {
+    // If 'light_vibrant_color_group' is white, then the hue should be ordered
+    // before all other hues.
+    hue = ash::IconColor::kHueMin;
+
+  } else {
+    SkScalar hsv[3];
+    SkColorToHSV(extracted_light_vibrant_color, hsv);
+    hue = hsv[0];
+
+    // If the hue is a red on the high end of the hsv color spectrum, then
+    // subtract the maximum possible hue so that reds on the high end of the hsv
+    // color spectrum are ordered next to reds on the low end of the hsv color
+    // spectrum.
+    if (hue >= kRedHueCutoff)
+      hue -= ash::IconColor::kHueMax;
+
+    // Shift up the hue value so that the returned hue value always remains
+    // within the interval [0, 360].
+    hue += (ash::IconColor::kHueMax - kRedHueCutoff);
+
+    DCHECK_GE(hue, 0);
+    DCHECK_LE(hue, ash::IconColor::kHueMax);
+  }
+
+  return ash::IconColor(background_color_group, hue);
+}
+
 }  // namespace reorder
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/reorder/app_list_reorder_util.h b/chrome/browser/ui/app_list/reorder/app_list_reorder_util.h
index a13f403..9334ab8f 100644
--- a/chrome/browser/ui/app_list/reorder/app_list_reorder_util.h
+++ b/chrome/browser/ui/app_list/reorder/app_list_reorder_util.h
@@ -71,6 +71,24 @@
 GenerateStringWrappersFromAppListItems(
     const std::vector<const ChromeAppListItem*>& app_list_items);
 
+// Used to calculate the color grouping of the icon image's background.
+// Samples color from the left, right, and top edge of the icon image and
+// determines the color group for each. Returns the most common grouping from
+// the samples. If all three sampled groups are different, then returns
+// 'light_vibrant_group' which is the color group for the light vibrant color of
+// the whole icon image.
+sync_pb::AppListSpecifics::ColorGroup CalculateBackgroundColorGroup(
+    const SkBitmap& source,
+    sync_pb::AppListSpecifics::ColorGroup light_vibrant_group);
+
+// Categorize `color` into one of the ColorGroups.
+sync_pb::AppListSpecifics::ColorGroup ColorToColorGroup(SkColor color);
+
+// Returns a SortableIconColor which can be used to sort icons based on a
+// combination of their background color and their light vibrant color.
+ash::IconColor GetSortableIconColorForApp(const std::string& id,
+                                          const gfx::ImageSkia& image);
+
 }  // namespace reorder
 }  // namespace app_list
 
diff --git a/chrome/browser/ui/app_list/reorder/app_list_reorder_util_unittest.cc b/chrome/browser/ui/app_list/reorder/app_list_reorder_util_unittest.cc
new file mode 100644
index 0000000..3fba52e
--- /dev/null
+++ b/chrome/browser/ui/app_list/reorder/app_list_reorder_util_unittest.cc
@@ -0,0 +1,87 @@
+// 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 "chrome/browser/ui/app_list/reorder/app_list_reorder_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/image/image_skia.h"
+
+using AppListReorderUtilTest = testing::Test;
+
+namespace app_list {
+
+gfx::ImageSkia GetRedIconWithBackgroundColorOf(SkColor color) {
+  const int width = 64;
+  const int height = 64;
+
+  SkBitmap icon;
+  icon.allocN32Pixels(width, height);
+  icon.eraseColor(color);
+
+  icon.erase(gfx::kGoogleRed300,
+             {width / 4, height / 4, width / 2, height / 2});
+
+  return gfx::ImageSkia::CreateFrom1xBitmap(icon);
+}
+
+TEST_F(AppListReorderUtilTest, SortableIconColorTest) {
+  const int red_hue = 49;
+
+  // Test a red icon with a black background color.
+  ash::IconColor group = reorder::GetSortableIconColorForApp(
+      "test_app1", GetRedIconWithBackgroundColorOf(SK_ColorBLACK));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_BLACK);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a white background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app2", GetRedIconWithBackgroundColorOf(SK_ColorWHITE));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_WHITE);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a red background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app3", GetRedIconWithBackgroundColorOf(SK_ColorRED));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_RED);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a yellow background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app4", GetRedIconWithBackgroundColorOf(SK_ColorYELLOW));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_YELLOW);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a green background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app5", GetRedIconWithBackgroundColorOf(SK_ColorGREEN));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_GREEN);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a blue background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app6", GetRedIconWithBackgroundColorOf(SK_ColorBLUE));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_BLUE);
+  EXPECT_EQ(group.hue(), red_hue);
+
+  // Test a red icon with a magenta background color.
+  group = reorder::GetSortableIconColorForApp(
+      "test_app7", GetRedIconWithBackgroundColorOf(SK_ColorMAGENTA));
+  EXPECT_EQ(group.background_color(),
+            sync_pb::AppListSpecifics::ColorGroup::
+                AppListSpecifics_ColorGroup_COLOR_MAGENTA);
+  EXPECT_EQ(group.hue(), red_hue);
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/ash/app_icon_color_cache.cc b/chrome/browser/ui/ash/app_icon_color_cache.cc
new file mode 100644
index 0000000..bee57b27
--- /dev/null
+++ b/chrome/browser/ui/ash/app_icon_color_cache.cc
@@ -0,0 +1,65 @@
+// 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 "chrome/browser/ui/ash/app_icon_color_cache.h"
+
+#include "ui/gfx/color_analysis.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace {
+
+// Uses the icon image to calculate the light vibrant color.
+absl::optional<SkColor> CalculateLightVibrantColor(gfx::ImageSkia image) {
+  const SkBitmap* source = image.bitmap();
+  if (!source || source->empty() || source->isNull())
+    return absl::nullopt;
+
+  std::vector<color_utils::ColorProfile> color_profiles;
+  color_profiles.push_back(color_utils::ColorProfile(
+      color_utils::LumaRange::LIGHT, color_utils::SaturationRange::VIBRANT));
+
+  std::vector<color_utils::Swatch> best_swatches =
+      color_utils::CalculateProminentColorsOfBitmap(
+          *source, color_profiles, nullptr /* bitmap region */,
+          color_utils::ColorSwatchFilter());
+
+  // If the best swatch color is transparent, then
+  // CalculateProminentColorsOfBitmap() failed to find a suitable color.
+  if (best_swatches.empty() || best_swatches[0].color == SK_ColorTRANSPARENT)
+    return absl::nullopt;
+
+  return best_swatches[0].color;
+}
+
+constexpr SkColor kDefaultLightVibrantColor = SK_ColorWHITE;
+
+}  // namespace
+
+namespace ash {
+
+AppIconColorCache& AppIconColorCache::GetInstance() {
+  static base::NoDestructor<AppIconColorCache> color_cache;
+  return *color_cache;
+}
+
+AppIconColorCache::AppIconColorCache() = default;
+
+AppIconColorCache::~AppIconColorCache() = default;
+
+SkColor AppIconColorCache::GetLightVibrantColorForApp(const std::string& app_id,
+                                                      gfx::ImageSkia icon) {
+  AppIdLightVibrantColor::const_iterator it =
+      app_id_light_vibrant_color_map_.find(app_id);
+  if (it != app_id_light_vibrant_color_map_.end())
+    return it->second;
+
+  SkColor light_vibrant_color =
+      CalculateLightVibrantColor(icon).value_or(kDefaultLightVibrantColor);
+  // TODO(crbug.com/1197249): Find a way to evict stale items in the
+  // AppIconColorCache.
+  app_id_light_vibrant_color_map_[app_id] = light_vibrant_color;
+  return light_vibrant_color;
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/ash/app_icon_color_cache.h b/chrome/browser/ui/ash/app_icon_color_cache.h
new file mode 100644
index 0000000..0d89d64
--- /dev/null
+++ b/chrome/browser/ui/ash/app_icon_color_cache.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_ASH_APP_ICON_COLOR_CACHE_H_
+#define CHROME_BROWSER_UI_ASH_APP_ICON_COLOR_CACHE_H_
+
+#include <map>
+#include <string>
+
+#include "base/no_destructor.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace gfx {
+class ImageSkia;
+}  // namespace gfx
+
+namespace ash {
+
+class AppIconColorCache {
+ public:
+  // Returns a reference to a singleton instance of AppIconColorCache.
+  static AppIconColorCache& GetInstance();
+
+  AppIconColorCache(const AppIconColorCache& other) = delete;
+  AppIconColorCache& operator=(const AppIconColorCache& other) = delete;
+  ~AppIconColorCache();
+
+  // Calculate the vibrant color for the app icon and cache it. If the |app_id|
+  // already has a cached color then return that instead.
+  SkColor GetLightVibrantColorForApp(const std::string& app_id,
+                                     gfx::ImageSkia icon);
+
+ private:
+  friend class base::NoDestructor<AppIconColorCache>;
+  AppIconColorCache();
+
+  using AppIdLightVibrantColor = std::map<std::string, SkColor>;
+  AppIdLightVibrantColor app_id_light_vibrant_color_map_;
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_UI_ASH_APP_ICON_COLOR_CACHE_H_
diff --git a/chrome/browser/ui/ash/notification_badge_color_cache_unittest.cc b/chrome/browser/ui/ash/app_icon_color_cache_unittest.cc
similarity index 70%
rename from chrome/browser/ui/ash/notification_badge_color_cache_unittest.cc
rename to chrome/browser/ui/ash/app_icon_color_cache_unittest.cc
index c9ed8e10..1eafce5 100644
--- a/chrome/browser/ui/ash/notification_badge_color_cache_unittest.cc
+++ b/chrome/browser/ui/ash/app_icon_color_cache_unittest.cc
@@ -4,16 +4,16 @@
 
 #include <memory>
 
-#include "chrome/browser/ui/ash/notification_badge_color_cache.h"
+#include "chrome/browser/ui/ash/app_icon_color_cache.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia.h"
 
-using NotificationBadgeTest = testing::Test;
+using AppIconColorCacheTestTest = testing::Test;
 
 namespace ash {
 
-TEST_F(NotificationBadgeTest, NotificationBadgeColorTest) {
+TEST_F(AppIconColorCacheTestTest, ExtractedLightVibrantColorTest) {
   const int width = 64;
   const int height = 64;
 
@@ -22,10 +22,10 @@
   all_black_icon.eraseColor(SK_ColorBLACK);
 
   SkColor test_color =
-      NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(
+      AppIconColorCache::GetInstance().GetLightVibrantColorForApp(
           "app_id1", gfx::ImageSkia::CreateFrom1xBitmap(all_black_icon));
 
-  // For an all black icon, a white notification badge is expected, since there
+  // For an all black icon, a default white color is expected, since there
   // is no other light vibrant color to get from the icon.
   EXPECT_EQ(test_color, SK_ColorWHITE);
 
@@ -35,10 +35,10 @@
   red_icon.eraseColor(gfx::kGoogleRed300);
   red_icon.erase(gfx::kGoogleRed600, {0, 0, width, height / 2});
 
-  test_color = NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(
+  test_color = AppIconColorCache::GetInstance().GetLightVibrantColorForApp(
       "app_id2", gfx::ImageSkia::CreateFrom1xBitmap(red_icon));
 
-  // For the red icon, the notification badge should calculate and use the
+  // For the red icon, the color cache should calculate and use the
   // kGoogleRed300 color as the light vibrant color taken from the icon.
   EXPECT_EQ(gfx::kGoogleRed300, test_color);
 }
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_app_launch_handler.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_app_launch_handler.cc
index e6c8e39c..2e3664f3 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_app_launch_handler.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_app_launch_handler.cc
@@ -206,6 +206,36 @@
   if (!ash::features::AreDesksTemplatesEnabled())
     return;
 
+  apps::AppRegistryCache& cache =
+      apps::AppServiceProxyFactory::GetForProfile(profile())
+          ->AppRegistryCache();
+
+  const auto& app_id_to_launch_list = restore_data()->app_id_to_launch_list();
+
+  // Add the ready ARC apps in `app_id_to_launch_list` to `app_ids`.
+  std::set<std::string> app_ids;
+  cache.ForEachApp(
+      [&app_ids, &app_id_to_launch_list](const apps::AppUpdate& update) {
+        if (update.Readiness() == apps::mojom::Readiness::kReady &&
+            update.AppType() == apps::mojom::AppType::kArc &&
+            base::Contains(app_id_to_launch_list, update.AppId())) {
+          app_ids.insert(update.AppId());
+        }
+      });
+
+  // For each ARC app, check and see if there is an existing instance. We will
+  // move this instance over instead of launching a new one. Remove the app from
+  // the restore data if it was successfully moved so that the ARC launch
+  // handler does not try to launch it later.
+  for (const std::string& app_id : app_ids) {
+    auto it = app_id_to_launch_list.find(app_id);
+    DCHECK(it != app_id_to_launch_list.end());
+    if (!ash::DesksController::Get()->OnSingleInstanceAppLaunchingFromTemplate(
+            app_id, it->second)) {
+      restore_data()->RemoveApp(app_id);
+    }
+  }
+
   auto* arc_task_handler =
       ash::app_restore::AppRestoreArcTaskHandler::GetForProfile(profile());
   if (!arc_task_handler)
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client.cc
index be14b85..64c2657 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client.cc
@@ -16,8 +16,9 @@
 #include "base/guid.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sync/desk_sync_service_factory.h"
 #include "chrome/browser/ui/ash/desks_templates/desks_templates_app_launch_handler.h"
@@ -47,6 +48,8 @@
     "The desk template has invalid or missing data.";
 constexpr char kStorageError[] = "The operation failed due to a storage error.";
 constexpr char kNoCurrentUserError[] = "There is no active profile.";
+constexpr char kBadProfileError[] =
+    "Either the profile is not valid or there is not an active proflile.";
 constexpr char kNoSavedTemplatesError[] = "You can create up to 6 templates.";
 
 // Returns true if |profile| is a supported profile in desk template feature.
@@ -151,6 +154,24 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void DesksTemplatesClient::GetTemplateJson(const std::string uuid,
+                                           Profile* profile,
+                                           GetTemplateJsonCallback callback) {
+  if (!active_profile_ || active_profile_ != profile) {
+    std::move(callback).Run(/*desk_templates=*/{},
+                            std::string(kBadProfileError));
+    return;
+  }
+
+  apps::AppRegistryCache& app_cache =
+      apps::AppServiceProxyFactory::GetForProfile(profile)->AppRegistryCache();
+
+  GetDeskModel()->GetTemplateJson(
+      uuid, &app_cache,
+      base::BindOnce(&DesksTemplatesClient::OnGetTemplateJson,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void DesksTemplatesClient::LaunchDeskTemplate(
     const std::string& template_uuid,
     LaunchDeskTemplateCallback callback) {
@@ -397,3 +418,14 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                      std::move(desk_template)));
 }
+
+void DesksTemplatesClient::OnGetTemplateJson(
+    DesksTemplatesClient::GetTemplateJsonCallback callback,
+    desks_storage::DeskModel::GetTemplateJsonStatus status,
+    const std::string& json_representation) {
+  std::move(callback).Run(
+      json_representation,
+      std::string(status != desks_storage::DeskModel::GetTemplateJsonStatus::kOk
+                      ? kStorageError
+                      : ""));
+}
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client.h b/chrome/browser/ui/ash/desks_templates/desks_templates_client.h
index 5c71776d..2069f40 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client.h
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
 #include "components/desks_storage/core/desk_model.h"
 
 class DesksTemplatesAppLaunchHandler;
@@ -26,8 +27,6 @@
 class LocalDeskDataManager;
 }  // namespace desks_storage
 
-class Profile;
-
 // Class to handle all Desks in-browser functionalities. Will call into
 // ash::DesksController to do actual desk related operations.
 class DesksTemplatesClient : public ash::SessionObserver {
@@ -83,6 +82,15 @@
   // Returns the current available saved desk templates.
   void GetDeskTemplates(GetDeskTemplatesCallback callback);
 
+  using GetTemplateJsonCallback =
+      base::OnceCallback<void(const std::string& template_json,
+                              std::string error)>;
+  // Takes in |uuid| and fetches the stringified json representation of a
+  // desk template.
+  void GetTemplateJson(const std::string uuid,
+                       Profile* profile,
+                       GetTemplateJsonCallback callback);
+
   using LaunchDeskTemplateCallback =
       base::OnceCallback<void(const std::string error)>;
   // Launches the desk template with |template_uuid| as a new desk.
@@ -168,6 +176,12 @@
   void OnCapturedDeskTemplate(CaptureActiveDeskAndSaveTemplateCallback callback,
                               std::unique_ptr<ash::DeskTemplate> desk_template);
 
+  // Callback function that handles the JSON representation of a specific
+  // template.
+  void OnGetTemplateJson(DesksTemplatesClient::GetTemplateJsonCallback callback,
+                         desks_storage::DeskModel::GetTemplateJsonStatus status,
+                         const std::string& json_representation);
+
   // Convenience pointer to ash::DesksController.
   // Guaranteed to be not null for the duration of `this`.
   ash::DesksController* const desks_controller_;
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index 60b816c..5b1e3b9 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -117,6 +117,21 @@
   return desk_template;
 }
 
+std::string GetTemplateJson(const std::string& uuid, Profile* profile) {
+  base::RunLoop run_loop;
+  std::string template_json_result;
+  DesksTemplatesClient::Get()->GetTemplateJson(
+      uuid, profile,
+      base::BindLambdaForTesting(
+          [&](const std::string& template_json, std::string error_string) {
+            run_loop.Quit();
+            ASSERT_TRUE(error_string.empty());
+            template_json_result = template_json;
+          }));
+  run_loop.Run();
+  return template_json_result;
+}
+
 void DeleteDeskTemplate(const base::GUID uuid) {
   base::RunLoop run_loop;
   DesksTemplatesClient::Get()->DeleteDeskTemplate(
@@ -1129,6 +1144,7 @@
 // - Deleting templates.
 // - Launching templates with uninstalled apps.
 // - Launching ARC apps.
+// - Launching ARC apps which already have an instance open.
 // - Port tests that use `DesksTemplatesClient` directly. These were meant to
 //   test launching while the prototype extension was being built, but now we
 //   can do end to end tests with the native UI.
@@ -1194,3 +1210,38 @@
   AddUser(account_id2_);
   EXPECT_EQ(get_templates_size(), 0);
 }
+
+// Tests that captured desk templates can be recalled as a JSON string.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest, GetDeskTemplateJson) {
+  // Test that Singleton was properly initialized.
+  ASSERT_TRUE(DesksTemplatesClient::Get());
+
+  // Change |browser|'s bounds.
+  const gfx::Rect browser_bounds = gfx::Rect(0, 0, 800, 200);
+  aura::Window* window = browser()->window()->GetNativeWindow();
+  window->SetBounds(browser_bounds);
+  // Make window visible on all desks.
+  window->SetProperty(aura::client::kWindowWorkspaceKey,
+                      aura::client::kWindowWorkspaceVisibleOnAllWorkspaces);
+
+  // Create the settings app, which is a system web app.
+  web_app::AppId settings_app_id =
+      CreateSettingsSystemWebApp(browser()->profile());
+
+  // Change the Settings app's bounds too.
+  const gfx::Rect settings_app_bounds = gfx::Rect(100, 100, 800, 300);
+  aura::Window* settings_window = FindBrowserWindow(kSettingsWindowId);
+  ASSERT_TRUE(settings_window);
+  settings_window->SetBounds(settings_app_bounds);
+
+  std::unique_ptr<ash::DeskTemplate> desk_template =
+      CaptureActiveDeskAndSaveTemplate();
+
+  std::string template_json = GetTemplateJson(
+      desk_template->uuid().AsLowercaseString(), browser()->profile());
+
+  // content of the conversion is tested in:
+  // components/desks_storage/core/desk_template_conversion_unittests.cc in this
+  // case we're simply interested in whether or not we got content back.
+  ASSERT_TRUE(!template_json.empty());
+}
diff --git a/chrome/browser/ui/ash/notification_badge_color_cache.cc b/chrome/browser/ui/ash/notification_badge_color_cache.cc
deleted file mode 100644
index 467cb8bdc7..0000000
--- a/chrome/browser/ui/ash/notification_badge_color_cache.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 "chrome/browser/ui/ash/notification_badge_color_cache.h"
-
-#include "base/no_destructor.h"
-#include "ui/gfx/color_analysis.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace {
-
-// Uses the icon image to calculate the light vibrant color to be used for
-// the notification indicator.
-absl::optional<SkColor> CalculateNotificationBadgeColor(gfx::ImageSkia image) {
-  const SkBitmap* source = image.bitmap();
-  if (!source || source->empty() || source->isNull())
-    return absl::nullopt;
-
-  std::vector<color_utils::ColorProfile> color_profiles;
-  color_profiles.push_back(color_utils::ColorProfile(
-      color_utils::LumaRange::LIGHT, color_utils::SaturationRange::VIBRANT));
-
-  std::vector<color_utils::Swatch> best_swatches =
-      color_utils::CalculateProminentColorsOfBitmap(
-          *source, color_profiles, nullptr /* bitmap region */,
-          color_utils::ColorSwatchFilter());
-
-  // If the best swatch color is transparent, then
-  // CalculateProminentColorsOfBitmap() failed to find a suitable color.
-  if (best_swatches.empty() || best_swatches[0].color == SK_ColorTRANSPARENT)
-    return absl::nullopt;
-
-  return best_swatches[0].color;
-}
-
-constexpr SkColor kDefaultIndicatorColor = SK_ColorWHITE;
-
-}  // namespace
-
-namespace ash {
-
-NotificationBadgeColorCache& NotificationBadgeColorCache::GetInstance() {
-  static base::NoDestructor<NotificationBadgeColorCache> color_cache;
-  return *color_cache;
-}
-
-NotificationBadgeColorCache::NotificationBadgeColorCache() = default;
-
-NotificationBadgeColorCache::~NotificationBadgeColorCache() = default;
-
-SkColor NotificationBadgeColorCache::GetBadgeColorForApp(
-    const std::string& app_id,
-    gfx::ImageSkia icon) {
-  AppIdBadgeColor::const_iterator it = app_id_badge_color_map_.find(app_id);
-  if (it != app_id_badge_color_map_.end())
-    return it->second;
-
-  SkColor notification_color =
-      CalculateNotificationBadgeColor(icon).value_or(kDefaultIndicatorColor);
-  // TODO(crbug.com/1197249): Find a way to evict stale items in the
-  // NotificationBadgeColorCache.
-  app_id_badge_color_map_[app_id] = notification_color;
-  return notification_color;
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ui/ash/notification_badge_color_cache.h b/chrome/browser/ui/ash/notification_badge_color_cache.h
deleted file mode 100644
index 8074567..0000000
--- a/chrome/browser/ui/ash/notification_badge_color_cache.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-
-#ifndef CHROME_BROWSER_UI_ASH_NOTIFICATION_BADGE_COLOR_CACHE_H_
-#define CHROME_BROWSER_UI_ASH_NOTIFICATION_BADGE_COLOR_CACHE_H_
-
-#include <map>
-#include <string>
-
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace gfx {
-class ImageSkia;
-}  // namespace gfx
-
-namespace ash {
-
-class NotificationBadgeColorCache {
- public:
-  // Returns a reference to a singleton instance of NotificationBadgeColorCache.
-  static NotificationBadgeColorCache& GetInstance();
-
-  NotificationBadgeColorCache();
-  NotificationBadgeColorCache(const NotificationBadgeColorCache& other) =
-      delete;
-  NotificationBadgeColorCache& operator=(
-      const NotificationBadgeColorCache& other) = delete;
-  ~NotificationBadgeColorCache();
-
-  // Calculate the color for the notification badge and cache it. If the
-  // |app_id| already has a cached color then return that instead.
-  SkColor GetBadgeColorForApp(const std::string& app_id, gfx::ImageSkia icon);
-
- private:
-  using AppIdBadgeColor = std::map<std::string, SkColor>;
-  AppIdBadgeColor app_id_badge_color_map_;
-};
-
-}  // namespace ash
-
-#endif  // CHROME_BROWSER_UI_ASH_NOTIFICATION_BADGE_COLOR_CACHE_H_
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
index c59daa2..f51f6fcc 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
@@ -52,10 +52,10 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/md_icon_normalizer.h"
 #include "chrome/browser/ui/apps/app_info_dialog.h"
+#include "chrome/browser/ui/ash/app_icon_color_cache.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
-#include "chrome/browser/ui/ash/notification_badge_color_cache.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h"
 #include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
@@ -455,7 +455,7 @@
     ash::ShelfItem new_item = *item;
     new_item.image = image;
     new_item.notification_badge_color =
-        ash::NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(
+        ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(
             new_item.id.app_id, image);
     model_->Set(model_->ItemIndexByID(shelf_id), new_item);
   }
@@ -1024,8 +1024,8 @@
     item.image = image;
     shelf_spinner_controller_->MaybeApplySpinningEffect(app_id, &item.image);
     item.notification_badge_color =
-        ash::NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(
-            app_id, image);
+        ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(app_id,
+                                                                         image);
     model_->Set(index, item);
     // It's possible we're waiting on more than one item, so don't break.
   }
@@ -1299,7 +1299,7 @@
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_CHROME_APP_ICON_192);
   browser_shortcut.notification_badge_color =
-      ash::NotificationBadgeColorCache::GetInstance().GetBadgeColorForApp(
+      ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(
           kChromeAppId, browser_shortcut.image);
   browser_shortcut.title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
 
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index c6425a7..43d14a7 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -65,6 +65,8 @@
 
 using chromeos::ProfileHelper;
 using extension_misc::kWallpaperManagerId;
+using file_manager::VolumeManager;
+using session_manager::SessionManager;
 using wallpaper_handlers::BackdropSurpriseMeImageFetcher;
 
 namespace {
@@ -222,10 +224,14 @@
 
   DCHECK(!g_wallpaper_controller_client_instance);
   g_wallpaper_controller_client_instance = this;
+
+  SessionManager* session_manager = SessionManager::Get();
+  // SessionManager might not exist in unit tests.
+  if (session_manager)
+    session_observation_.Observe(session_manager);
 }
 
 WallpaperControllerClientImpl::~WallpaperControllerClientImpl() {
-  user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
   wallpaper_controller_->SetClient(nullptr);
   weak_factory_.InvalidateWeakPtrs();
   DCHECK_EQ(this, g_wallpaper_controller_client_instance);
@@ -240,7 +246,6 @@
           &WallpaperControllerClientImpl::DeviceWallpaperImageFilePathChanged,
           weak_factory_.GetWeakPtr()));
   wallpaper_controller_ = ash::WallpaperController::Get();
-  user_manager::UserManager::Get()->AddSessionStateObserver(this);
 
   InitController();
 }
@@ -282,7 +287,7 @@
   }
 
   // Show a white wallpaper during OOBE.
-  if (session_manager::SessionManager::Get()->session_state() ==
+  if (SessionManager::Get()->session_state() ==
       session_manager::SessionState::OOBE) {
     SkBitmap bitmap;
     bitmap.allocN32Pixels(1, 1);
@@ -524,10 +529,9 @@
 bool WallpaperControllerClientImpl::IsWallpaperSyncEnabled(
     const AccountId& account_id) const {
   Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
-  // Profile isn't created yet. We shouldn't try syncing.
-  // This can happen on new user login.
   if (!profile)
     return false;
+
   syncer::SyncService* sync_service =
       SyncServiceFactory::GetForProfile(profile);
   if (!sync_service)
@@ -543,24 +547,32 @@
              syncer::UserSelectableType::kThemes);
 }
 
-void WallpaperControllerClientImpl::ActiveUserChanged(
-    user_manager::User* active_user) {
-  volume_manager_observation_.Reset();
-  active_user->AddProfileCreatedObserver(
-      base::BindOnce(&WallpaperControllerClientImpl::OnProfileCreated,
-                     weak_factory_.GetWeakPtr(), active_user));
-}
-
 void WallpaperControllerClientImpl::OnVolumeMounted(
     chromeos::MountError error_code,
     const file_manager::Volume& volume) {
+  if (error_code != chromeos::MOUNT_ERROR_NONE) {
+    return;
+  }
   if (volume.type() != file_manager::VolumeType::VOLUME_TYPE_GOOGLE_DRIVE) {
     return;
   }
-  user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
-  if (user) {
-    wallpaper_controller_->SyncLocalAndRemotePrefs(user->GetAccountId());
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  CHECK(profile);
+  VolumeManager* volume_manager = VolumeManager::Get(profile);
+  // Volume ID is based on the mount path, which for drive is based on the
+  // account_id.
+  if (!volume_manager->FindVolumeById(volume.volume_id())) {
+    return;
   }
+  user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
+  CHECK(user);
+  wallpaper_controller_->SyncLocalAndRemotePrefs(user->GetAccountId());
+}
+
+void WallpaperControllerClientImpl::OnUserProfileLoaded(
+    const AccountId& account_id) {
+  wallpaper_controller_->SyncLocalAndRemotePrefs(account_id);
+  ObserveVolumeManagerForAccountId(account_id);
 }
 
 void WallpaperControllerClientImpl::MigrateCollectionIdFromValueStoreForTesting(
@@ -761,21 +773,11 @@
   images_info_fetcher_.reset();
 }
 
-void WallpaperControllerClientImpl::OnProfileCreated(user_manager::User* user) {
-  if (!user->is_active())
-    return;
-
-  ObserveVolumeManagerForActiveUser(user);
-  wallpaper_controller_->SyncLocalAndRemotePrefs(user->GetAccountId());
-}
-
-void WallpaperControllerClientImpl::ObserveVolumeManagerForActiveUser(
-    user_manager::User* user) {
-  Profile* profile = ProfileHelper::Get()->GetProfileByUser(user);
-  DCHECK(profile);
-
-  volume_manager_observation_.Observe(
-      file_manager::VolumeManager::Get(profile));
+void WallpaperControllerClientImpl::ObserveVolumeManagerForAccountId(
+    const AccountId& account_id) {
+  Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
+  CHECK(profile);
+  volume_manager_observation_.AddObservation(VolumeManager::Get(profile));
 }
 
 void WallpaperControllerClientImpl::RecordWallpaperSourceUMA(
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
index 37ff821..c649e60e 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.h
@@ -11,12 +11,15 @@
 #include "ash/public/cpp/wallpaper/wallpaper_controller.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
+#include "base/scoped_multi_source_observation.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/ash/file_manager/volume_manager_observer.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
 #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
 #include "components/prefs/pref_change_registrar.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/session_manager/core/session_manager_observer.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "url/gurl.h"
@@ -34,8 +37,8 @@
 // Handles chrome-side wallpaper control alongside the ash-side controller.
 class WallpaperControllerClientImpl
     : public ash::WallpaperControllerClient,
-      public user_manager::UserManager::UserSessionStateObserver,
-      public file_manager::VolumeManagerObserver {
+      public file_manager::VolumeManagerObserver,
+      public session_manager::SessionManagerObserver {
  public:
   WallpaperControllerClientImpl();
 
@@ -78,13 +81,13 @@
                       files_id_callback) const override;
   bool IsWallpaperSyncEnabled(const AccountId& account_id) const override;
 
-  // user_manager::UserManager::UserSessionStateObserver:
-  void ActiveUserChanged(user_manager::User* active_user) override;
-
   // file_manager::VolumeManagerObserver:
   void OnVolumeMounted(chromeos::MountError error_code,
                        const file_manager::Volume& volume) override;
 
+  // session_manager::SessionManagerObserver implementation.
+  void OnUserProfileLoaded(const AccountId& account_id) override;
+
   // Wrappers around the ash::WallpaperController interface.
   void SetCustomWallpaper(const AccountId& account_id,
                           const std::string& file_name,
@@ -177,8 +180,8 @@
                                   bool success,
                                   const std::string& collection_id,
                                   const std::vector<backdrop::Image>& images);
-  void OnProfileCreated(user_manager::User* user);
-  void ObserveVolumeManagerForActiveUser(user_manager::User* user);
+
+  void ObserveVolumeManagerForAccountId(const AccountId& account_id);
 
   // WallpaperController interface in ash.
   ash::WallpaperController* wallpaper_controller_;
@@ -199,9 +202,12 @@
   std::unique_ptr<wallpaper_handlers::BackdropImageInfoFetcher>
       images_info_fetcher_;
 
-  base::ScopedObservation<file_manager::VolumeManager,
-                          file_manager::VolumeManagerObserver>
+  base::ScopedMultiSourceObservation<file_manager::VolumeManager,
+                                     file_manager::VolumeManagerObserver>
       volume_manager_observation_{this};
+  base::ScopedObservation<session_manager::SessionManager,
+                          session_manager::SessionManagerObserver>
+      session_observation_{this};
 
   base::WeakPtrFactory<WallpaperControllerClientImpl> weak_factory_{this};
   base::WeakPtrFactory<WallpaperControllerClientImpl> storage_weak_factory_{
diff --git a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
index eb57a44..d7ddc8f8 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
@@ -85,7 +85,7 @@
 
     DictionaryPrefUpdate dict_update(local_state,
                                      prefs::kTaskManagerColumnVisibility);
-    dict_update->Clear();
+    dict_update->DictClear();
   }
 
   void ToggleColumnVisibility(TaskManagerMac* task_manager, int col_id) {
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index 69c7c8ec..c4eadbf3 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -346,16 +346,16 @@
         !SessionStartupPref::TypeHasRecommendedValue(profile_->GetPrefs());
   }
 
+  // TODO(https://crbug.com/1276034): Cleanup this code, in particular on Ash
+  // where the welcome flow is never shown.
   bool welcome_enabled = true;
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-  welcome_enabled =
-      welcome::IsEnabled(profile_) && welcome::HasModulesToShow(profile_);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_))
     welcome_enabled = false;
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif !BUILDFLAG(IS_CHROMEOS_ASH)
+  welcome_enabled =
+      welcome::IsEnabled(profile_) && welcome::HasModulesToShow(profile_);
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
   const bool whats_new_enabled =
       promotional_tabs_enabled && whats_new::ShouldShowForState(local_state);
diff --git a/chrome/browser/ui/tabs/tab_activity_simulator.cc b/chrome/browser/ui/tabs/tab_activity_simulator.cc
index 38209b4..a08ac05 100644
--- a/chrome/browser/ui/tabs/tab_activity_simulator.cc
+++ b/chrome/browser/ui/tabs/tab_activity_simulator.cc
@@ -51,7 +51,7 @@
 std::unique_ptr<content::WebContents> TabActivitySimulator::CreateWebContents(
     content::BrowserContext* browser_context,
     bool initially_visible) {
-  content::WebContents::CreateParams params(browser_context, nullptr);
+  content::WebContents::CreateParams params(browser_context);
   params.initially_hidden = !initially_visible;
   std::unique_ptr<content::WebContents> test_contents(
       content::WebContentsTester::CreateTestWebContents(params));
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
index bb610f8..2cc8742 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -105,6 +105,8 @@
       BookmarkBubbleView::bookmark_bubble()->GetFootnoteViewForTesting());
 }
 
+// TODO(https://crbug.com/1260291): Add support for Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Verifies that the sync promo is displayed for a user that is not signed in.
 TEST_F(BookmarkBubbleViewTest, SyncPromoNotSignedIn) {
   CreateBubbleView();
@@ -116,3 +118,4 @@
   EXPECT_TRUE(footnote);
 #endif
 }
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 32a5413..1f700ee 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -3900,7 +3900,8 @@
   profiles::BubbleViewMode bubble_view_mode;
   profiles::BubbleViewModeFromAvatarBubbleMode(mode, GetProfile(),
                                                &bubble_view_mode);
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// TODO(https://crbug.com/1260291): Add support for Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   if (SigninViewController::ShouldShowSigninForMode(bubble_view_mode)) {
     browser_->signin_view_controller()->ShowSignin(bubble_view_mode,
                                                    access_point);
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc
index a4dfb74..38a15dd 100644
--- a/chrome/browser/ui/views/task_manager_view_browsertest.cc
+++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -88,7 +88,7 @@
 
     DictionaryPrefUpdate dict_update(local_state,
                                      prefs::kTaskManagerColumnVisibility);
-    dict_update->Clear();
+    dict_update->DictClear();
   }
 
   void ToggleColumnVisibility(TaskManagerView* view, int col_id) {
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index a641279..0b66105 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -1518,7 +1518,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     WebAppIntegrationBrowserTest,
-    WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
+    DISABLED_WebAppIntegration_InstCrtShctWindowedSiteA_WindowCreated_InListWinSiteA_InstPlcyTabShctSiteA_InListWinSiteA_LaunchFromIconSiteA_WindowCreated) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index 6b10068..e259e360 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -64,6 +64,7 @@
 #include "base/base64.h"
 #include "base/cxx17_backports.h"
 #include "base/strings/strcat.h"
+#include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/crostini/crostini_manager.h"
 #include "chrome/browser/ash/customization/customization_document.h"
@@ -72,10 +73,19 @@
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/component_updater/cros_component_manager.h"
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/language/core/common/locale_util.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #endif
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/lacros/lacros_url_handling.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+#if defined(OS_CHROMEOS)
+#include "chrome/common/webui_url_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif  // defined(OS_CHROMEOS)
 
 using content::BrowserThread;
 
@@ -498,6 +508,52 @@
   }
 }
 
+#if defined(OS_CHROMEOS)
+// This function returns true if Lacros is the primary browser - or if the
+// calling browser is Lacros.
+bool isLacrosPrimaryOrCurrentBrowser() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  return crosapi::browser_util::IsLacrosPrimaryBrowser();
+#else
+  return true;
+#endif
+}
+
+void AppendBody(std::string* output) {
+  if (isLacrosPrimaryOrCurrentBrowser()) {
+    output->append(
+        "<link rel='stylesheet' href='chrome://resources/css/os_header.css'>\n"
+
+        "</head>\n<body>\n"
+
+        "<div class='os-link-container-container' id='os-link-container'>\n"
+        "<div class='os-link-container'>\n"
+        "<span class='os-link-icon'></span>\n"
+        "<span aria-hidden='true' id='os-link-desc'>" +
+        l10n_util::GetStringUTF8(IDS_ABOUT_OS_TEXT1_LABEL) +
+        "</span>\n"
+        "<a href='#' id='os-link-href' aria-describedby='os-link-desc'>" +
+        l10n_util::GetStringUTF8(IDS_ABOUT_OS_LINK) +
+        "</a>\n<span aria-hidden='true'>" +
+        l10n_util::GetStringUTF8(IDS_ABOUT_OS_TEXT2_LABEL) +
+        "</span>\n</div>\n</div>\n");
+  } else {
+    output->append("</head>\n<body>\n");
+  }
+}
+
+void AppendFooter(std::string* output) {
+  if (isLacrosPrimaryOrCurrentBrowser()) {
+    output->append(
+        "<script type='module' src='chrome://resources/js/os_about.js'>"
+        "</script>\n");
+  }
+
+  output->append("</body>\n</html>\n");
+}
+
+#else  // !defined(OS_CHROMEOS)
+
 void AppendBody(std::string *output) {
   output->append("</head>\n<body>\n");
 }
@@ -506,6 +562,8 @@
   output->append("</body>\n</html>\n");
 }
 
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace about_ui
 
 using about_ui::AppendHeader;
@@ -526,11 +584,12 @@
   std::sort(hosts.begin(), hosts.end());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  const bool is_lacros_primary = about_ui::isLacrosPrimaryOrCurrentBrowser();
   // If Lacros is active, the user can navigate by hand to os:// URL's but
   // internally we will still navigate to chrome:// URL's. Note also that
   // only a subset of URLs might be available in this mode - so we have to
   // make sure that only allowed URLs are being presented.
-  if (crosapi::browser_util::IsLacrosPrimaryBrowser()) {
+  if (is_lacros_primary) {
     auto* WebUiControllerFactory = ChromeWebUIControllerFactory::GetInstance();
     for (const std::string& host : hosts) {
       // TODO(crbug/1271718): The refactor should make sure that the provided
@@ -573,7 +632,7 @@
   // internally we will still navigate to chrome:// URL's. Note also that
   // only a subset of URLs might be available in this mode - so we have to
   // make sure that only allowed URLs are being presented.
-  if (crosapi::browser_util::IsLacrosPrimaryBrowser()) {
+  if (is_lacros_primary) {
     auto* WebUiControllerFactory = ChromeWebUIControllerFactory::GetInstance();
     for (size_t i = 0; i < chrome::kNumberOfChromeDebugURLs; i++) {
       // TODO(crbug/1271718): The refactor should make sure that the provided
@@ -735,3 +794,23 @@
   content::URLDataSource::Add(
       profile, std::make_unique<AboutUIHTMLSource>(name, profile));
 }
+
+#if defined(OS_CHROMEOS)
+
+bool AboutUI::OverrideHandleWebUIMessage(const GURL& source_url,
+                                         const std::string& message,
+                                         const base::ListValue& args) {
+  if (message != "crosUrlAboutRedirect")
+    return false;
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  lacros_url_handling::NavigateInAsh(GURL(chrome::kOsUIAboutURL));
+#else
+  // Note: This will only be called by the UI when Lacros is available.
+  DCHECK(crosapi::BrowserManager::Get());
+  crosapi::BrowserManager::Get()->OpenUrl(GURL(chrome::kChromeUIAboutURL));
+#endif
+  return true;
+}
+
+#endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/about_ui.h b/chrome/browser/ui/webui/about_ui.h
index 9bcc9eb2..782a1d7 100644
--- a/chrome/browser/ui/webui/about_ui.h
+++ b/chrome/browser/ui/webui/about_ui.h
@@ -54,7 +54,13 @@
   AboutUI(const AboutUI&) = delete;
   AboutUI& operator=(const AboutUI&) = delete;
 
-  ~AboutUI() override {}
+  ~AboutUI() override = default;
+
+#if defined(OS_CHROMEOS)
+  bool OverrideHandleWebUIMessage(const GURL& source_url,
+                                  const std::string& message,
+                                  const base::ListValue& args) override;
+#endif
 };
 
 namespace about_ui {
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
index 936a18db..7f7b790 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
@@ -14,9 +14,12 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/access_code_cast_resources.h"
 #include "chrome/grit/access_code_cast_resources_map.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/bindings_policy.h"
+#include "ui/base/webui/web_ui_util.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 //  AccessCodeCast dialog:
@@ -110,6 +113,16 @@
       base::make_span(kAccessCodeCastResources, kAccessCodeCastResourcesSize),
       IDR_ACCESS_CODE_CAST_INDEX_HTML);
 
+  static constexpr webui::LocalizedString kStrings[] = {
+      {"back", IDS_ACCESS_CODE_CAST_BACK},
+      {"cast", IDS_ACCESS_CODE_CAST_CAST},
+      {"close", IDS_CLOSE},
+      {"dialogTitle", IDS_ACCESS_CODE_CAST_DIALOG_TITLE},
+      {"useCamera", IDS_ACCESS_CODE_CAST_USE_CAMERA},
+  };
+
+  source->AddLocalizedStrings(kStrings);
+
   content::BrowserContext* browser_context =
       web_ui->GetWebContents()->GetBrowserContext();
   content::WebUIDataSource::Add(browser_context, source.release());
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index ad7c67b2..02517ef 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -149,7 +149,7 @@
  public:
   ChromeURLDataManagerWebUITrustedTypesTest() {
     std::vector<base::Feature> enabled_features;
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
     if (GetParam() == std::string("chrome://welcome"))
       enabled_features.push_back(welcome::kForceEnabled);
 #endif
@@ -331,10 +331,10 @@
 #if !defined(OS_CHROMEOS)
     "chrome://apps",
     "chrome://browser-switch",
+    "chrome://welcome",
 #endif
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
     "chrome://signin-email-confirmation",
-    "chrome://welcome",
 #endif
 #if !defined(OS_MAC)
     "chrome://sandbox",
diff --git a/chrome/browser/ui/webui/components/components_handler.cc b/chrome/browser/ui/webui/components/components_handler.cc
index ea60e6e..dbe5432 100644
--- a/chrome/browser/ui/webui/components/components_handler.cc
+++ b/chrome/browser/ui/webui/components/components_handler.cc
@@ -15,6 +15,17 @@
 #include "components/update_client/crx_update_item.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/common/webui_url_constants.h"
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/crosapi/browser_manager.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chrome/common/webui_url_constants.h"
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/lacros/lacros_url_handling.h"
+#endif
+#endif  // defined(OS_CHROMEOS)
+
 ComponentsHandler::ComponentsHandler(
     component_updater::ComponentUpdateService* component_updater)
     : component_updater_(component_updater) {
@@ -31,6 +42,13 @@
   web_ui()->RegisterDeprecatedMessageCallback(
       "checkUpdate", base::BindRepeating(&ComponentsHandler::HandleCheckUpdate,
                                          base::Unretained(this)));
+
+#if defined(OS_CHROMEOS)
+  web_ui()->RegisterDeprecatedMessageCallback(
+      "crosUrlComponentsRedirect",
+      base::BindRepeating(&ComponentsHandler::HandleCrosUrlComponentsRedirect,
+                          base::Unretained(this)));
+#endif
 }
 
 void ComponentsHandler::OnJavascriptAllowed() {
@@ -49,6 +67,16 @@
   base::DictionaryValue result;
   result.SetKey("components",
                 base::Value::FromUniquePtrValue(LoadComponents()));
+
+#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  const bool showSystemFlagsLink = crosapi::browser_util::IsLacrosEnabled();
+#else
+  const bool showSystemFlagsLink = true;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  result.SetBoolean("showOsLink", showSystemFlagsLink);
+#endif  // defined(OS_CHROMEOS)
+
   ResolveJavascriptCallback(callback_id, result);
 }
 
@@ -143,6 +171,19 @@
   return l10n_util::GetStringUTF16(IDS_COMPONENTS_UNKNOWN);
 }
 
+#if defined(OS_CHROMEOS)
+void ComponentsHandler::HandleCrosUrlComponentsRedirect(
+    const base::ListValue* args) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  lacros_url_handling::NavigateInAsh(GURL(chrome::kOsUIComponentsUrl));
+#else
+  // Note: This will only be called by the UI when Lacros is available.
+  DCHECK(crosapi::BrowserManager::Get());
+  crosapi::BrowserManager::Get()->OpenUrl(GURL(chrome::kChromeUIComponentsUrl));
+#endif
+}
+#endif  // defined(OS_CHROMEOS)
+
 void ComponentsHandler::OnDemandUpdate(const std::string& component_id) {
   component_updater_->GetOnDemandUpdater().OnDemandUpdate(
       component_id, component_updater::OnDemandUpdater::Priority::FOREGROUND,
diff --git a/chrome/browser/ui/webui/components/components_handler.h b/chrome/browser/ui/webui/components/components_handler.h
index d19a721..900b899 100644
--- a/chrome/browser/ui/webui/components/components_handler.h
+++ b/chrome/browser/ui/webui/components/components_handler.h
@@ -42,6 +42,11 @@
   // ServiceObserver implementation.
   void OnEvent(Events event, const std::string& id) override;
 
+#if defined(OS_CHROMEOS)
+  // Callback for the "crosUrlComponentsRedirect" message.
+  void HandleCrosUrlComponentsRedirect(const base::ListValue* args);
+#endif
+
  private:
   static std::u16string ComponentEventToString(Events event);
   static std::u16string ServiceStatusToString(
diff --git a/chrome/browser/ui/webui/components/components_ui.cc b/chrome/browser/ui/webui/components/components_ui.cc
index 92a2fb7..c6e87025 100644
--- a/chrome/browser/ui/webui/components/components_ui.cc
+++ b/chrome/browser/ui/webui/components/components_ui.cc
@@ -53,13 +53,18 @@
       "trusted-types jstemplate parse-html-subset;");
 
   static constexpr webui::LocalizedString kStrings[] = {
-      {"componentsTitle", IDS_COMPONENTS_TITLE},
-      {"componentsNoneInstalled", IDS_COMPONENTS_NONE_INSTALLED},
-      {"componentVersion", IDS_COMPONENTS_VERSION},
-      {"checkUpdate", IDS_COMPONENTS_CHECK_FOR_UPDATE},
-      {"noComponents", IDS_COMPONENTS_NO_COMPONENTS},
-      {"statusLabel", IDS_COMPONENTS_STATUS_LABEL},
-      {"checkingLabel", IDS_COMPONENTS_CHECKING_LABEL},
+    {"componentsTitle", IDS_COMPONENTS_TITLE},
+    {"componentsNoneInstalled", IDS_COMPONENTS_NONE_INSTALLED},
+    {"componentVersion", IDS_COMPONENTS_VERSION},
+    {"checkUpdate", IDS_COMPONENTS_CHECK_FOR_UPDATE},
+    {"noComponents", IDS_COMPONENTS_NO_COMPONENTS},
+    {"statusLabel", IDS_COMPONENTS_STATUS_LABEL},
+    {"checkingLabel", IDS_COMPONENTS_CHECKING_LABEL},
+#if defined(OS_CHROMEOS)
+    {"os-components-text1", IDS_COMPONENTS_OS_TEXT1_LABEL},
+    {"os-components-text2", IDS_COMPONENTS_OS_TEXT2_LABEL},
+    {"os-components-link", IDS_COMPONENTS_OS_LINK},
+#endif
   };
   source->AddLocalizedStrings(kStrings);
 
diff --git a/chrome/browser/ui/webui/flags/flags_ui.cc b/chrome/browser/ui/webui/flags/flags_ui.cc
index dea2bdf0..7bcfd68 100644
--- a/chrome/browser/ui/webui/flags/flags_ui.cc
+++ b/chrome/browser/ui/webui/flags/flags_ui.cc
@@ -89,9 +89,6 @@
 
   source->AddResourcePath(flags_ui::kFlagsJS, IDR_FLAGS_UI_FLAGS_JS);
   source->AddResourcePath(flags_ui::kFlagsCSS, IDR_FLAGS_UI_FLAGS_CSS);
-#if defined(OS_CHROMEOS)
-  source->AddResourcePath(flags_ui::kFlagsSVG, IDR_OS_FLAGS_UI_FLAGS_SVG);
-#endif
   source->SetDefaultResource(IDR_FLAGS_UI_FLAGS_HTML);
   source->UseStringsJs();
   return source;
diff --git a/chrome/browser/ui/webui/history/foreign_session_handler.cc b/chrome/browser/ui/webui/history/foreign_session_handler.cc
index 05c520c..75cda14 100644
--- a/chrome/browser/ui/webui/history/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/history/foreign_session_handler.cc
@@ -314,7 +314,7 @@
     base::DictionaryValue* current_collapsed_sessions = pref_update.Get();
     std::unique_ptr<base::DictionaryValue> collapsed_sessions(
         current_collapsed_sessions->DeepCopy());
-    current_collapsed_sessions->Clear();
+    current_collapsed_sessions->DictClear();
 
     // Note: we don't own the SyncedSessions themselves.
     for (size_t i = 0; i < sessions.size() && i < kMaxSessionsToShow; ++i) {
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index fa6a7d9..7771150 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -196,7 +196,7 @@
   // The items which are to be written into |value| are also described in
   // chrome/browser/resources/ntp4/page_list_view.js in @typedef for AppInfo.
   // Please update it whenever you add or remove any keys here.
-  value->Clear();
+  value->DictClear();
 
   // Communicate the kiosk flag so the apps page can disable showing the
   // context menu in kiosk mode.
@@ -306,7 +306,7 @@
   // The items which are to be written into |value| are also described in
   // chrome/browser/resources/ntp4/page_list_view.js in @typedef for AppInfo.
   // Please update it whenever you add or remove any keys here.
-  value->Clear();
+  value->DictClear();
 
   // Communicate the kiosk flag so the apps page can disable showing the
   // context menu in kiosk mode.
diff --git a/chrome/browser/ui/webui/realbox/realbox_handler.cc b/chrome/browser/ui/webui/realbox/realbox_handler.cc
index bf8a84b9..fed5184 100644
--- a/chrome/browser/ui/webui/realbox/realbox_handler.cc
+++ b/chrome/browser/ui/webui/realbox/realbox_handler.cc
@@ -242,13 +242,13 @@
               match.action->GetVectorIcon()));
     }
     mojom_match->a11y_label = AutocompleteMatchType::ToAccessibilityLabel(
-        match, match.contents, has_action, line,
+        match, match.contents, line, 0,
         GetAdditionalA11yMessage(match,
                                  RealboxHandler::FocusState::kFocusedMatch));
 
     mojom_match->remove_button_a11y_label =
         AutocompleteMatchType::ToAccessibilityLabel(
-            match, match.contents, false, line,
+            match, match.contents, line, 0,
             GetAdditionalA11yMessage(
                 match,
                 RealboxHandler::FocusState::kFocusedButtonRemoveSuggestion));
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index 34b10657..ade8543 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -1247,9 +1247,7 @@
   handler_->OnDidClosePage(&did_abort);
 }
 
-// TODO(crbug.com/1220066): Remove the lacros exclusion when DICE is disabled on
-// Lacros.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 TEST(PeopleHandlerDiceUnifiedConsentTest, StoredAccountsList) {
   ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
 
@@ -1291,7 +1289,7 @@
   EXPECT_EQ("a@gmail.com", accounts_list[0].FindKey("email")->GetString());
   EXPECT_EQ("b@gmail.com", accounts_list[1].FindKey("email")->GetString());
 }
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Regression test for crash in guest mode. https://crbug.com/1040476
diff --git a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
index 5e0509a2..6437e2f 100644
--- a/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
+++ b/chrome/browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc
@@ -611,6 +611,8 @@
   CheckDelegateCalls();
 }
 
+// TODO(https://crbug.com/1260291): Enable this test on Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Tests that the login error is displayed and that the account is removed.
 TEST_F(DiceTurnSyncOnHelperTest, CanOfferSigninErrorRemoveAccount) {
   // Set expectations.
@@ -627,6 +629,7 @@
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id()));
   CheckDelegateCalls();
 }
+#endif
 
 // Tests that the sync disabled message is displayed and that the account is
 // removed upon the ABORT_SYNC action.
@@ -718,6 +721,8 @@
   CheckDelegateCalls();
 }
 
+// TODO(https://crbug.com/1260291): Enable this test on Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Aborts the flow after the cross account dialog.
 TEST_F(DiceTurnSyncOnHelperTest, CrossAccountAbort) {
   // Set expectations.
@@ -735,6 +740,7 @@
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id()));
   CheckDelegateCalls();
 }
+#endif
 
 // Merge data after the cross account dialog.
 TEST_F(DiceTurnSyncOnHelperTest, CrossAccountContinue) {
@@ -755,6 +761,8 @@
   CheckDelegateCalls();
 }
 
+// TODO(https://crbug.com/1260291): Enable these tests on Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Create a new profile after the cross account dialog and show the signin page.
 TEST_F(DiceTurnSyncOnHelperTest, CrossAccountNewProfile) {
   // Set expectations.
@@ -796,6 +804,7 @@
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id()));
   CheckDelegateCalls();
 }
+#endif
 
 // Continue after the enterprise confirmation prompt.
 TEST_F(DiceTurnSyncOnHelperTest, DISABLED_EnterpriseConfirmationContinue) {
@@ -816,6 +825,8 @@
   CheckDelegateCalls();
 }
 
+// TODO(https://crbug.com/1260291): Enable this test on Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Continue with a new profile after the enterprise confirmation prompt.
 TEST_F(DiceTurnSyncOnHelperTest, EnterpriseConfirmationNewProfile) {
   // Set expectations.
@@ -873,6 +884,7 @@
                               signin::ConsentLevel::kSignin));
   CheckDelegateCalls();
 }
+#endif
 
 // Tests that the sync confirmation is shown and the user can abort.
 TEST_F(DiceTurnSyncOnHelperTest, UndoSync) {
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 89525dd..467bbc5 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -442,7 +442,7 @@
 bool SignInWithUI(Browser* browser,
                   const std::string& username,
                   const std::string& password) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   NOTREACHED();
   return false;
 #else
diff --git a/chrome/browser/ui/webui/version/version_handler_chromeos.cc b/chrome/browser/ui/webui/version/version_handler_chromeos.cc
index 76f9c72e..bd851e1 100644
--- a/chrome/browser/ui/webui/version/version_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/version/version_handler_chromeos.cc
@@ -8,8 +8,23 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "chrome/common/channel_info.h"
+#include "chrome/common/webui_url_constants.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/web_ui.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/crosapi/browser_manager.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/lacros/lacros_url_handling.h"
+#endif
+
+namespace {
+
+const char kCrosUrlVersionRedirect[] = "crosUrlVersionRedirect";
+
+}  // namespace
 
 VersionHandlerChromeOS::VersionHandlerChromeOS() {}
 
@@ -19,6 +34,7 @@
     const base::ListValue* args) {
   VersionHandler::HandleRequestVersionInfo(args);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Start the asynchronous load of the versions.
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
@@ -36,8 +52,36 @@
       base::BindOnce(&chromeos::version_loader::GetARCVersion),
       base::BindOnce(&VersionHandlerChromeOS::OnARCVersion,
                      weak_factory_.GetWeakPtr()));
+
+  const bool showSystemFlagsLink = crosapi::browser_util::IsLacrosEnabled();
+#else
+  const bool showSystemFlagsLink = true;
+#endif
+
+  FireWebUIListener("return-lacros-primary", base::Value(showSystemFlagsLink));
 }
 
+void VersionHandlerChromeOS::RegisterMessages() {
+  VersionHandler::RegisterMessages();
+
+  web_ui()->RegisterDeprecatedMessageCallback(
+      kCrosUrlVersionRedirect,
+      base::BindRepeating(&VersionHandlerChromeOS::HandleCrosUrlVersionRedirect,
+                          base::Unretained(this)));
+}
+
+void VersionHandlerChromeOS::HandleCrosUrlVersionRedirect(
+    const base::ListValue* args) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  lacros_url_handling::NavigateInAsh(GURL(chrome::kOsUIVersionURL));
+#else
+  // Note: This will only be called by the UI when Lacros is available.
+  DCHECK(crosapi::BrowserManager::Get());
+  crosapi::BrowserManager::Get()->OpenUrl(GURL(chrome::kChromeUIVersionURL));
+#endif
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 void VersionHandlerChromeOS::OnVersion(const std::string& version) {
   FireWebUIListener("return-os-version", base::Value(version));
 }
@@ -49,3 +93,4 @@
 void VersionHandlerChromeOS::OnARCVersion(const std::string& version) {
   FireWebUIListener("return-arc-version", base::Value(version));
 }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/version/version_handler_chromeos.h b/chrome/browser/ui/webui/version/version_handler_chromeos.h
index 6f0ff95..cb8abf2 100644
--- a/chrome/browser/ui/webui/version/version_handler_chromeos.h
+++ b/chrome/browser/ui/webui/version/version_handler_chromeos.h
@@ -24,12 +24,16 @@
 
   // VersionHandler overrides:
   void HandleRequestVersionInfo(const base::ListValue* args) override;
+  void RegisterMessages() override;
 
   // Callbacks from chromeos::VersionLoader.
   void OnVersion(const std::string& version);
   void OnOSFirmware(const std::string& version);
   void OnARCVersion(const std::string& version);
 
+  // Callback for the "crosUrlVersionRedirect" message.
+  void HandleCrosUrlVersionRedirect(const base::ListValue* args);
+
  private:
   base::WeakPtrFactory<VersionHandlerChromeOS> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/webui/version/version_ui.cc b/chrome/browser/ui/webui/version/version_ui.cc
index b0ddb595..a16629b 100644
--- a/chrome/browser/ui/webui/version/version_ui.cc
+++ b/chrome/browser/ui/webui/version/version_ui.cc
@@ -41,9 +41,9 @@
 #include "chrome/browser/ui/webui/theme_source.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
 #include "chrome/browser/ui/webui/version/version_handler_chromeos.h"
-#endif
+#endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_MAC)
 #include "base/mac/mac_util.h"
@@ -81,6 +81,11 @@
 #else
     {version_ui::kOSName, IDS_VERSION_UI_OS},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
+    {version_ui::kOsVersionHeaderText1, IDS_VERSION_UI_OS_TEXT1_LABEL},
+    {version_ui::kOsVersionHeaderText2, IDS_VERSION_UI_OS_TEXT2_LABEL},
+    {version_ui::kOsVersionHeaderLink, IDS_VERSION_UI_OS_LINK},
+#endif  // defined(OS_CHROMEOS)
 #if defined(OS_ANDROID)
     {version_ui::kGmsName, IDS_VERSION_UI_GMS},
 #endif  // OS_ANDROID
@@ -93,6 +98,7 @@
   html_source->AddResourcePath(version_ui::kVersionJS, IDR_VERSION_UI_JS);
   html_source->AddResourcePath(version_ui::kAboutVersionCSS,
                                IDR_VERSION_UI_CSS);
+
 #if defined(OS_ANDROID)
   html_source->AddResourcePath(version_ui::kAboutVersionMobileCSS,
                                IDR_VERSION_UI_MOBILE_CSS);
@@ -110,7 +116,7 @@
     : content::WebUIController(web_ui) {
   Profile* profile = Profile::FromWebUI(web_ui);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   web_ui->AddMessageHandler(std::make_unique<VersionHandlerChromeOS>());
 #elif defined(OS_WIN)
   web_ui->AddMessageHandler(std::make_unique<VersionHandlerWindows>());
diff --git a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
index c768aa5..a80918d 100644
--- a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
+++ b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
@@ -340,7 +340,8 @@
 }
 #endif  // !defined(OS_MAC)
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// TODO(https://crbug.com/1260291): Add support for Lacros.
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 // Regression test: crbug.com/438979.
 IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
                        SettingsZoomAfterSigninWorks) {
@@ -394,7 +395,7 @@
   zoom_controller->SetZoomLevel(new_zoom_level);
   zoom_change_watcher.Wait();
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 class ZoomControllerForPrerenderingTest : public ZoomControllerBrowserTest,
                                           public zoom::ZoomObserver {
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
index 057f1bb..410bf4f 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
@@ -15,6 +15,7 @@
 #include "base/scoped_observation.h"
 #include "base/types/id_type.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_icon/app_icon_factory.h"
 #include "chrome/browser/apps/app_service/app_icon/icon_key_util.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host.h b/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
index 08cb9cd..9ade0c8 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
@@ -13,6 +13,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/web_applications/app_registrar_observer.h"
 #include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h"
 #include "chrome/browser/web_applications/web_app_id.h"
diff --git a/chrome/browser/web_applications/daily_metrics_helper.cc b/chrome/browser/web_applications/daily_metrics_helper.cc
index 797dc3d..3bf3cc7 100644
--- a/chrome/browser/web_applications/daily_metrics_helper.cc
+++ b/chrome/browser/web_applications/daily_metrics_helper.cc
@@ -178,7 +178,7 @@
   if (!urls_to_features)
     return;
   DictionaryPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
-  update->Clear();
+  update->DictClear();
 }
 
 void UpdateRecord(DailyInteraction& record, PrefService* prefs) {
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index fc5f827..415ae77 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1638900013-db184c25401df262649790cdae5da13614a59acd.profdata
+chrome-linux-main-1638921351-e750e85447e5a4785820e3c0c259a4e848f17376.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index a80e016a..f26b54a8 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1638900013-010ce28736fd8ab0074480388494541e79eec8bb.profdata
+chrome-mac-main-1638921351-e4f84217ae51eceaa3265e8409e7da035913e3d4.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 23c0269..7e7eff7 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1638900013-7b4fa9227e1500597c04f339b6c0f220bba38bfc.profdata
+chrome-win32-main-1638910746-6f2059065ea0a285d7f23a81a91d274c627cc874.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 63cbb2e..10c4a2da 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1638900013-d1c807884ad964426f81cb800819d31ca57389a5.profdata
+chrome-win64-main-1638910746-95f0b6aa9a203ecb25b642f88c10b4a064d6c812.profdata
diff --git a/chrome/common/extensions/api/input_ime.json b/chrome/common/extensions/api/input_ime.json
index b0a8078..bce78ed 100644
--- a/chrome/common/extensions/api/input_ime.json
+++ b/chrome/common/extensions/api/input_ime.json
@@ -6,6 +6,7 @@
   {
     "namespace": "input.ime",
     "description": "Use the <code>chrome.input.ime</code> API to implement a custom IME for Chrome OS. This allows your extension to handle keystrokes, set the composition, and manage the candidate window.",
+    "platforms": ["chromeos"],
     "types": [
       {
         "id": "KeyboardEventType",
diff --git a/chrome/common/extensions/api/wm_desks_private.idl b/chrome/common/extensions/api/wm_desks_private.idl
index f6eae00..a36b3c17 100644
--- a/chrome/common/extensions/api/wm_desks_private.idl
+++ b/chrome/common/extensions/api/wm_desks_private.idl
@@ -18,6 +18,7 @@
   callback GetSavedDeskTemplatesCallback =
       void (DeskTemplate[] deskTemplates);
   callback DeskTemplateVoidCallback = void ();
+  callback GetDeskTemplateJsonCallback = void (DOMString templateJson);
 
   interface Functions {
     // Captures the current active desk as a template and then returns the
@@ -51,5 +52,11 @@
     // returned.
     static void launchDeskTemplate(DOMString templateUuid,
                                    DeskTemplateVoidCallback callback);
+
+    // Gets the template associated wit the templateUuid and returns its JSON
+    // representation.  Returns an error if either the template could not be
+    // found or the user profile is not valid.
+    static void getDeskTemplateJson(DOMString templateUuid,
+                                    GetDeskTemplateJsonCallback callback);
   };
 };
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 6297db0..2d26c1b 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -350,7 +350,6 @@
 const char kOsUICroshURL[] = "os://crosh";
 const char kOsUIFileManagerURL[] = "os://file-manager";
 const char kOsUITerminalURL[] = "os://terminal";
-const char kOsUIAboutURL[] = "os://about";
 const char kOsUIAccountManagerErrorURL[] = "os://account-manager-error";
 const char kOsUIAccountManagerWelcomeURL[] = "os://account-manager-welcome";
 const char kOsUIAccountMigrationWelcomeURL[] = "os://account-migration-welcome";
@@ -361,7 +360,6 @@
 const char kOsUIArcPowerControlURL[] = "os://arc-power-control";
 const char kOsUIAssistantOptInURL[] = "os://assistant-optin";
 const char kOsUIBluetoothPairingURL[] = "os://bluetooth-pairing";
-const char kOsUIComponentsUrl[] = "os://components";
 const char kOsUICrashesUrl[] = "os://crashes";
 const char kOsUICreditsURL[] = "os://credits";
 const char kOsUICrostiniCreditsURL[] = "os://crostini-credits";
@@ -397,7 +395,6 @@
 const char kOsUISyncInternalsUrl[] = "os://sync-internals";
 const char kOsUISysInternalsUrl[] = "os://sys-internals";
 const char kOsUIUserImageURL[] = "os://userimage";
-const char kOsUIVersionURL[] = "os://version";
 const char kOsUIVmUrl[] = "os://vm";
 
 // Keep alphabetized.
@@ -450,7 +447,10 @@
 const char kChromeUIAppDisabledHost[] = "app-disabled";
 const char kChromeUIOSSettingsHost[] = "os-settings";
 const char kChromeUIOSSettingsURL[] = "chrome://os-settings/";
+const char kOsUIAboutURL[] = "os://about";
+const char kOsUIComponentsUrl[] = "os://components";
 const char kOsUIFlagsURL[] = "os://flags";
+const char kOsUIVersionURL[] = "os://version";
 #endif
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index a76df7b0..4cd418d2 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -333,7 +333,6 @@
 extern const char kOsUICroshURL[];
 extern const char kOsUIFileManagerURL[];
 extern const char kOsUITerminalURL[];
-extern const char kOsUIAboutURL[];
 extern const char kOsUIAccountManagerErrorURL[];
 extern const char kOsUIAccountManagerWelcomeURL[];
 extern const char kOsUIAccountMigrationWelcomeURL[];
@@ -344,7 +343,6 @@
 extern const char kOsUIArcPowerControlURL[];
 extern const char kOsUIAssistantOptInURL[];
 extern const char kOsUIBluetoothPairingURL[];
-extern const char kOsUIComponentsUrl[];
 extern const char kOsUICrashesUrl[];
 extern const char kOsUICreditsURL[];
 extern const char kOsUICrostiniCreditsURL[];
@@ -380,7 +378,6 @@
 extern const char kOsUISyncInternalsUrl[];
 extern const char kOsUISysInternalsUrl[];
 extern const char kOsUIUserImageURL[];
-extern const char kOsUIVersionURL[];
 extern const char kOsUIVmUrl[];
 
 // Returns true if this web UI is part of the "system UI". Generally this is
@@ -394,7 +391,10 @@
 extern const char kChromeUIAppDisabledHost[];
 extern const char kChromeUIOSSettingsHost[];
 extern const char kChromeUIOSSettingsURL[];
+extern const char kOsUIAboutURL[];
+extern const char kOsUIComponentsUrl[];
 extern const char kOsUIFlagsURL[];
+extern const char kOsUIVersionURL[];
 #endif
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 980d071..699d167 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1753,7 +1753,6 @@
       "../browser/policy/test/policy_browsertest.cc",
       "../browser/policy/test/policy_statistics_collector_browsertest.cc",
       "../browser/policy/test/policy_test_google_browsertest.cc",
-      "../browser/policy/test/promotional_tabs_enabled_policy_browsertest.cc",
       "../browser/policy/test/proxy_policies_browsertest.cc",
       "../browser/policy/test/restore_on_startup_policy_browsertest.cc",
       "../browser/policy/test/safe_browsing_policy_browsertest.cc",
@@ -2056,7 +2055,6 @@
       "../browser/ui/webui/support_tool_ui_browsertest.cc",
       "../browser/ui/webui/tab_search/tab_search_ui_browsertest.cc",
       "../browser/ui/webui/webui_load_timer_browsertest.cc",
-      "../browser/ui/webui/webui_webview_browsertest.cc",
       "../browser/ui/zoom/zoom_controller_browsertest.cc",
       "../browser/ukm_worker_browsertest.cc",
       "../browser/unload_browsertest.cc",
@@ -2139,6 +2137,9 @@
         # https://crbug.com/1252812 The intent picker (launch icon) actions
         # are not working on Lacros.
         "../browser/ui/views/web_apps/web_app_integration_browsertest.cc",
+
+        # Lacros does not seem to have any actual WebView-based UI to test.
+        "../browser/ui/webui/webui_webview_browsertest.cc",
       ]
     }
 
@@ -2209,7 +2210,6 @@
         "../browser/importer/edge_importer_browsertest_win.cc",
         "../browser/importer/ie_importer_browsertest_win.cc",
         "../browser/net/chrome_mojo_proxy_resolver_win_browsertest.cc",
-        "../browser/policy/test/locale_policy_browsertest.cc",
         "../browser/printing/pdf_to_emf_converter_browsertest.cc",
         "../browser/process_singleton_browsertest.cc",
         "../browser/profiles/profile_shortcut_manager_browsertest_win.cc",
@@ -2310,7 +2310,6 @@
     } else {
       # !is_mac
       sources += [
-        "../browser/policy/test/full_screen_allowed_policy_browsertest.cc",
         "../browser/ui/views/create_application_shortcut_view_browsertest.cc",
       ]
 
@@ -2353,10 +2352,8 @@
     if (!is_chromeos_ash) {
       sources += [
         "../browser/external_protocol/external_protocol_handler_browsertest.cc",
-        "../browser/policy/test/hardware_acceleration_mode_enabled_browsertest.cc",
         "../browser/policy/test/variation_restrict_parameter_policy_browsertest.cc",
         "../browser/profiles/profile_window_browsertest.cc",
-        "../browser/ui/signin_reauth_view_controller_browsertest.cc",
         "../browser/ui/views/accessibility/accessibility_focus_highlight_browsertest.cc",
         "../browser/ui/views/profiles/signin_view_controller_delegate_views_browsertest.cc",
       ]
@@ -2447,17 +2444,13 @@
         "../browser/signin/dice_browsertest.cc",
         "../browser/signin/dice_web_signin_interceptor_browsertest.cc",
         "../browser/signin/signin_ui_util_browsertest.cc",
+        "../browser/ui/signin_reauth_view_controller_browsertest.cc",
+        "../browser/ui/views/sync/inline_login_ui_browsertest.cc",
         "../browser/unified_consent/unified_consent_browsertest.cc",
       ]
       if (is_win) {
         sources += [ "../browser/signin/signin_util_win_browsertest.cc" ]
       }
-
-      # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is
-      # no longer defined on Lacros.
-      if (is_chromeos_lacros) {
-        sources -= [ "../browser/signin/signin_ui_util_browsertest.cc" ]
-      }
     } else {
       sources += [ "../browser/signin/mirror_browsertest.cc" ]
     }
@@ -2986,7 +2979,6 @@
         "../browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc",
         "../browser/ui/views/session_crashed_bubble_view_browsertest.cc",
         "../browser/ui/views/status_bubble_views_browsertest.cc",
-        "../browser/ui/views/sync/inline_login_ui_browsertest.cc",
         "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc",
         "../browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc",
         "../browser/ui/views/tabs/tab_group_editor_bubble_view_browsertest.cc",
@@ -3458,15 +3450,8 @@
         "../browser/notifications/notifier_settings_test_observer.cc",
         "../browser/notifications/notifier_settings_test_observer.h",
         "../browser/policy/login_policy_test_base_browsertest.cc",
-        "../browser/policy/test/accessibility_policy_browsertest.cc",
-        "../browser/policy/test/arc_policy_browsertest.cc",
-        "../browser/policy/test/assistant_policy_browsertest.cc",
-        "../browser/policy/test/audio_output_allowed_browsertest.cc",
         "../browser/policy/test/note_taking_on_lock_screen_policy_browsertest.cc",
-        "../browser/policy/test/session_length_limit_policy_browsertest.cc",
-        "../browser/policy/test/suggested_content_policy_browsertest.cc",
         "../browser/policy/test/system_features_policy_browsertest.cc",
-        "../browser/policy/test/unified_desktop_enabled_browsertest.cc",
         "../browser/process_singleton_browsertest.cc",
         "../browser/renderer_context_menu/quick_answers_menu_observer_browsertest.cc",
         "../browser/sessions/session_restore_browsertest_chromeos.cc",
@@ -3660,9 +3645,6 @@
 
         # chromeos does not use the profile chooser view
         "../browser/ui/views/profiles/profile_menu_view_browsertest.cc",
-
-        # inline login UI is disabled on chromeos
-        "../browser/ui/views/sync/inline_login_ui_browsertest.cc",
         "../browser/ui/views/sync/profile_signin_confirmation_dialog_views_browsertest.cc",
         "../browser/ui/webui/profile_helper_browsertest.cc",
 
@@ -5728,19 +5710,10 @@
       "../browser/signin/process_dice_header_delegate_impl_unittest.cc",
       "../browser/signin/signin_manager_unittest.cc",
       "../browser/ui/passwords/account_storage_auth_helper_unittest.cc",
+      "../browser/ui/startup/startup_browser_policy_unittest.cc",
       "../browser/ui/views/profiles/dice_web_signin_interception_bubble_view_unittest.cc",
       "../browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc",
     ]
-
-    # TODO(https://crbug.com/1198523: Remove this once enable_dice_support is no
-    # longer defined on Lacros.
-    if (is_chromeos_lacros) {
-      sources -= [
-        "../browser/password_manager/multi_profile_credentials_filter_unittest.cc",
-        "../browser/signin/dice_web_signin_interceptor_unittest.cc",
-        "../browser/signin/signin_manager_unittest.cc",
-      ]
-    }
   }
 
   if (is_win || is_mac || (is_linux || is_chromeos_lacros)) {
@@ -6617,6 +6590,7 @@
       "../browser/ui/app_list/arc/mock_arc_app_list_prefs_observer.h",
       "../browser/ui/app_list/chrome_app_list_item_manager_unittest.cc",
       "../browser/ui/app_list/md_icon_normalizer_unittest.cc",
+      "../browser/ui/app_list/reorder/app_list_reorder_util_unittest.cc",
       "../browser/ui/app_list/search/app_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
@@ -6658,6 +6632,7 @@
       "../browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc",
       "../browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc",
       "../browser/ui/ash/ambient/ambient_client_impl_unittest.cc",
+      "../browser/ui/ash/app_icon_color_cache_unittest.cc",
       "../browser/ui/ash/assistant/assistant_state_client_unittest.cc",
       "../browser/ui/ash/assistant/device_actions_unittest.cc",
       "../browser/ui/ash/assistant/search_and_assistant_enabled_checker_unittest.cc",
@@ -6677,7 +6652,6 @@
       "../browser/ui/ash/network/network_portal_notification_controller_unittest.cc",
       "../browser/ui/ash/network/network_state_notifier_unittest.cc",
       "../browser/ui/ash/network/tether_notification_presenter_unittest.cc",
-      "../browser/ui/ash/notification_badge_color_cache_unittest.cc",
       "../browser/ui/ash/projector/projector_client_impl_unittest.cc",
       "../browser/ui/ash/projector/projector_soda_installation_controller_unittest.cc",
       "../browser/ui/ash/quick_answers/quick_answers_controller_unittest.cc",
@@ -7316,9 +7290,6 @@
       "../browser/ui/startup/startup_tab_provider_unittest.cc",
     ]
   }
-  if (!is_android && !is_chromeos_ash) {
-    sources += [ "../browser/ui/startup/startup_browser_policy_unittest.cc" ]
-  }
 
   if (use_gio) {
     configs += [ "//build/linux:gio_config" ]
@@ -8302,6 +8273,7 @@
       "../browser/extensions/api/notifications/notifications_apitest.cc",
       "../browser/extensions/api/omnibox/omnibox_api_interactive_test.cc",
       "../browser/extensions/api/tabs/tabs_interactive_test.cc",
+      "../browser/extensions/app_window_uitest.cc",
       "../browser/extensions/chrome_extension_test_notification_observer.cc",
       "../browser/extensions/chrome_extension_test_notification_observer.h",
       "../browser/extensions/extension_apitest.cc",
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index cfcaed28..3afb4d74 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -212,7 +212,7 @@
     if (status.IsError())
       return status;
 
-    params.Clear();
+    params.DictClear();
     params.SetString("expression", script);
     status = SendCommandAndIgnoreResponse("Runtime.evaluate", params);
     if (status.IsError())
diff --git a/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc b/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
index 8a1388e..699ed1a 100644
--- a/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/dom_tracker_unittest.cc
@@ -85,7 +85,7 @@
   ASSERT_TRUE(tracker.GetFrameIdForNode(1, &frame_id).IsError());
   ASSERT_TRUE(frame_id.empty());
 
-  params.Clear();
+  params.DictClear();
   params.Set("node", base::JSONReader::ReadDeprecated(
                          "{\"nodeId\":2,\"frameId\":\"f\"}"));
   ASSERT_EQ(kOk,
diff --git a/chrome/test/chromedriver/chrome/frame_tracker.cc b/chrome/test/chromedriver/chrome/frame_tracker.cc
index 9cdf0de..bccad6f 100644
--- a/chrome/test/chromedriver/chrome/frame_tracker.cc
+++ b/chrome/test/chromedriver/chrome/frame_tracker.cc
@@ -84,7 +84,7 @@
   if (status.IsError())
     return status;
   // Enable runtime events to allow tracking execution context creation.
-  params.Clear();
+  params.DictClear();
   status = client->SendCommand("Runtime.enable", params);
   if (status.IsError())
     return status;
diff --git a/chrome/test/chromedriver/chrome/frame_tracker_unittest.cc b/chrome/test/chromedriver/chrome/frame_tracker_unittest.cc
index b73b8236..d2f20546 100644
--- a/chrome/test/chromedriver/chrome/frame_tracker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/frame_tracker_unittest.cc
@@ -36,7 +36,7 @@
   ASSERT_EQ(kOk,
             tracker.OnEvent(&client, "Page.frameNavigated", nav_params).code());
   ASSERT_TRUE(tracker.GetContextIdForFrame("f", &context_id).IsOk());
-  nav_params.Clear();
+  nav_params.DictClear();
   ASSERT_EQ(kOk,
             tracker.OnEvent(&client, "Page.frameNavigated", nav_params).code());
   ASSERT_EQ(kNoSuchExecutionContext,
diff --git a/chrome/test/chromedriver/chrome/recorder_devtools_client.h b/chrome/test/chromedriver/chrome/recorder_devtools_client.h
index aea4bfe6..38e7c3d9 100644
--- a/chrome/test/chromedriver/chrome/recorder_devtools_client.h
+++ b/chrome/test/chromedriver/chrome/recorder_devtools_client.h
@@ -27,7 +27,7 @@
   }
   Command& operator=(const Command& command) {
     method = command.method;
-    params.Clear();
+    params.DictClear();
     params.MergeDictionary(&command.params);
     return *this;
   }
diff --git a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
index c05240b..6c30896 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
@@ -33,7 +33,7 @@
     status_ = status;
   }
   void set_result(const base::DictionaryValue& result) {
-    result_.Clear();
+    result_.DictClear();
     result_.MergeDictionary(&result);
   }
 
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 872a6bed..5b0e74a 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -430,7 +430,7 @@
   CHECK(always_match);
   CHECK(first_match);
   CHECK(merged);
-  merged->Clear();
+  merged->DictClear();
 
   for (base::DictionaryValue::Iterator it(*first_match); !it.IsAtEnd();
        it.Advance()) {
diff --git a/chrome/test/chromedriver/session_commands_unittest.cc b/chrome/test/chromedriver/session_commands_unittest.cc
index 727a86d..b2c628d 100644
--- a/chrome/test/chromedriver/session_commands_unittest.cc
+++ b/chrome/test/chromedriver/session_commands_unittest.cc
@@ -68,13 +68,13 @@
   status = ExecuteSetTimeouts(&session, params, &value);
   ASSERT_EQ(kInvalidArgument, status.code());
 
-  params.Clear();
+  params.DictClear();
   params.SetInteger("unknown", 5000);
   status = ExecuteSetTimeouts(&session, params, &value);
   ASSERT_EQ(kOk, status.code());
 
   // Old pre-W3C format.
-  params.Clear();
+  params.DictClear();
   params.SetDoubleKey("ms", 5000.0);
   params.SetString("type", "page load");
   status = ExecuteSetTimeouts(&session, params, &value);
@@ -98,7 +98,7 @@
   // non key collision should return true
   ASSERT_TRUE(MergeCapabilities(&primary, &secondary, &merged));
 
-  merged.Clear();
+  merged.DictClear();
   MergeCapabilities(&primary, &secondary, &merged);
   primary.MergeDictionary(&secondary);
 
@@ -371,7 +371,7 @@
 
   ASSERT_FALSE(MatchCapabilities(&merged));
 
-  merged.Clear();
+  merged.DictClear();
   merged.SetString("browserName", "chrome");
 
   ASSERT_TRUE(MatchCapabilities(&merged));
@@ -388,7 +388,7 @@
   EXPECT_FALSE(MatchCapabilities(&merged));
 
   // Don't match values other than bools.
-  merged.Clear();
+  merged.DictClear();
   merged.SetStringPath("webauthn:virtualAuthenticators", "not a bool");
   EXPECT_FALSE(MatchCapabilities(&merged));
 }
@@ -404,7 +404,7 @@
   EXPECT_FALSE(MatchCapabilities(&merged));
 
   // Don't match values other than bools.
-  merged.Clear();
+  merged.DictClear();
   merged.SetStringPath("webauthn:extension:largeBlob", "not a bool");
   EXPECT_FALSE(MatchCapabilities(&merged));
 }
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index d707594..cf22fb8 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -1933,7 +1933,7 @@
   }
 
   session->input_cancel_list.clear();
-  session->input_state_table.Clear();
+  session->input_state_table.DictClear();
   session->active_input_sources.ClearList();
   session->mouse_position = WebPoint(0, 0);
   session->click_count = 0;
diff --git a/chrome/test/data/extensions/api_test/autotest_private/test.js b/chrome/test/data/extensions/api_test/autotest_private/test.js
index d05639c2..551cfd6 100644
--- a/chrome/test/data/extensions/api_test/autotest_private/test.js
+++ b/chrome/test/data/extensions/api_test/autotest_private/test.js
@@ -1368,7 +1368,7 @@
 
   async function pinThenUnpinFileApp() {
     // Pin the File app.
-    var fileID = 'hhaomjibdihmijegdhdafkllkbggdgoj'
+    var fileID = 'unique-file-id-123'
     var pinResults = await promisify(
         chrome.autotestPrivate.setShelfIconPin,
         [{appId: fileID, pinned: true}]);
diff --git a/chrome/test/data/webui/access_code_cast/access_code_cast_browsertest.js b/chrome/test/data/webui/access_code_cast/access_code_cast_browsertest.js
index 6d490bd..8af144d1 100644
--- a/chrome/test/data/webui/access_code_cast/access_code_cast_browsertest.js
+++ b/chrome/test/data/webui/access_code_cast/access_code_cast_browsertest.js
@@ -43,3 +43,19 @@
 TEST_F('AccessCodeCastAppTest', 'All', function() {
   mocha.run();
 });
+
+// eslint-disable-next-line no-var
+var AccessCodeCastCodeInputElementTest = class extends AccessCodeCastBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://access-code-cast/test_loader.html?module=access_code_cast/code_input_test.js';
+  }
+};
+
+/**
+ * This browsertest acts as a thin wrapper to run the unit tests found
+ * at code_input_test.js
+ */
+TEST_F('AccessCodeCastCodeInputElementTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/access_code_cast/code_input_test.js b/chrome/test/data/webui/access_code_cast/code_input_test.js
new file mode 100644
index 0000000..2d793e3
--- /dev/null
+++ b/chrome/test/data/webui/access_code_cast/code_input_test.js
@@ -0,0 +1,32 @@
+// 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.
+
+import 'chrome://access-code-cast/code_input/code_input.js';
+
+suite('CodeInputElementTest', () => {
+  /** @type {!CodeInputElement} */
+  let c2cInput;
+
+  /** @type {!CrInputElement} */
+  let crInput;
+
+  setup(() => {
+    PolymerTest.clearBody();
+
+    c2cInput = document.createElement('c2c-code-input');
+    document.body.appendChild(c2cInput);
+    crInput = c2cInput.crInput;
+  });
+
+  test('value set correctly', () => {
+    c2cInput.value = 'hello';
+    assertEquals(c2cInput.value, crInput.value);
+
+    // |value| is copied to uppercase when typing triggers inputEvent.
+    let testString = 'hello world';
+    crInput.value = testString;
+    crInput.dispatchEvent(new InputEvent('input'));
+    assertEquals(c2cInput.value, testString.toUpperCase());
+  });
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/tab_search/tab_search_item_test.js b/chrome/test/data/webui/tab_search/tab_search_item_test.js
index 9efa079..131facb 100644
--- a/chrome/test/data/webui/tab_search/tab_search_item_test.js
+++ b/chrome/test/data/webui/tab_search/tab_search_item_test.js
@@ -8,7 +8,6 @@
 import {flushTasks} from '../test_util.js';
 
 import {sampleToken} from './tab_search_test_data.js';
-import {typed} from './tab_search_test_helper.js';
 
 suite('TabSearchItemTest', () => {
   /** @type {!TabSearchItem} */
@@ -18,7 +17,7 @@
   async function setupTest(data) {
     tabSearchItem = /** @type {!TabSearchItem} */ (
         document.createElement('tab-search-item'));
-    tabSearchItem.data = typed(data, TabData);
+    tabSearchItem.data = data;
     document.body.innerHTML = '';
     document.body.appendChild(tabSearchItem);
     await flushTasks();
@@ -31,24 +30,23 @@
    */
   async function assertTabSearchItemHighlights(
       text, fieldHighlightRanges, expected) {
-    const data = /** @type {!TabData} */ ({
-      hostname: text,
-      tab: {
-        active: true,
-        index: 0,
-        isDefaultFavicon: true,
-        lastActiveTimeTicks: {internalValue: BigInt(0)},
-        pinned: false,
-        showIcon: true,
-        tabId: 0,
-        url: {url: 'https://example.com'},
-        title: text,
-      },
-      highlightRanges: {
-        'tab.title': fieldHighlightRanges,
-        hostname: fieldHighlightRanges,
-      },
-    });
+    const data = new TabData(
+        {
+          active: true,
+          index: 0,
+          isDefaultFavicon: true,
+          lastActiveTimeTicks: {internalValue: BigInt(0)},
+          pinned: false,
+          showIcon: true,
+          tabId: 0,
+          url: {url: 'https://example.com'},
+          title: text,
+        },
+        TabItemType.OPEN_TAB, text);
+    data.highlightRanges = {
+      'tab.title': fieldHighlightRanges,
+      hostname: fieldHighlightRanges,
+    };
     await setupTest(data);
 
     assertHighlight(
@@ -84,32 +82,33 @@
   });
 
   test('CloseButtonPresence', async () => {
-    const tab = /** @type {!Tab} */ ({
-      active: true,
-      alertStates: [],
-      index: 0,
-      isDefaultFavicon: true,
-      lastActiveTimeTicks: {internalValue: BigInt(0)},
-      pinned: false,
-      showIcon: true,
-      tabId: 0,
-      url: {url: 'https://example.com'},
-      title: 'Example.com site',
-    });
-
-    await setupTest(/** @type {!TabData} */ ({
-      hostname: 'example',
-      tab,
-      type: TabItemType.OPEN_TAB,
-      highlightRanges: {},
-    }));
+    await setupTest(new TabData(
+        {
+          active: true,
+          alertStates: [],
+          index: 0,
+          isDefaultFavicon: true,
+          lastActiveTimeTicks: {internalValue: BigInt(0)},
+          pinned: false,
+          showIcon: true,
+          tabId: 0,
+          url: {url: 'https://example.com'},
+          title: 'Example.com site',
+        },
+        TabItemType.OPEN_TAB, 'example'));
 
     let tabSearchItemCloseButton = /** @type {!HTMLElement} */ (
         tabSearchItem.shadowRoot.querySelector('cr-icon-button'));
     assertNotEquals(null, tabSearchItemCloseButton);
 
-    await setupTest(/** @type {!TabData} */ (
-        {hostname: 'example', tab, type: TabItemType.RECENTLY_CLOSED_TAB}));
+    await setupTest(new TabData(
+        {
+          tabId: 0,
+          title: 'Example.com site',
+          url: {url: 'https://example.com'},
+          lastActiveTimeTicks: {internalValue: BigInt(0)},
+        },
+        TabItemType.RECENTLY_CLOSED_TAB, 'example'));
 
     tabSearchItemCloseButton = /** @type {!HTMLElement} */ (
         tabSearchItem.shadowRoot.querySelector('cr-icon-button'));
@@ -138,13 +137,9 @@
       title: 'Examples',
     });
 
-    await setupTest(/** @type {!TabData} */ ({
-      hostname: 'example',
-      tab,
-      type: TabItemType.OPEN_TAB,
-      tabGroup,
-      highlightRanges: {},
-    }));
+    const tabData = new TabData(tab, TabItemType.OPEN_TAB, 'example');
+    tabData.tabGroup = tabGroup;
+    await setupTest(tabData);
 
     const groupDotElement = tabSearchItem.shadowRoot.querySelector('#groupDot');
     assertNotEquals(null, groupDotElement);
@@ -173,12 +168,7 @@
       title: 'Example.com site',
     });
 
-    await setupTest(/** @type {!TabData} */ ({
-      hostname: 'example',
-      tab,
-      type: TabItemType.OPEN_TAB,
-      highlightRanges: {},
-    }));
+    await setupTest(new TabData(tab, TabItemType.OPEN_TAB, 'example'));
 
     const recordingMediaAlert =
         tabSearchItem.shadowRoot.querySelector('#mediaAlert');
@@ -205,12 +195,7 @@
       title: 'Example.com site',
     });
 
-    await setupTest(/** @type {!TabData} */ ({
-      hostname: 'example',
-      tab,
-      type: TabItemType.OPEN_TAB,
-      highlightRanges: {},
-    }));
+    await setupTest(new TabData(tab, TabItemType.OPEN_TAB, 'example'));
 
     const audioMediaAlert =
         tabSearchItem.shadowRoot.querySelector('#mediaAlert');
diff --git a/chrome/test/data/webui/tab_search/tab_search_test_helper.js b/chrome/test/data/webui/tab_search/tab_search_test_helper.js
index a112030a..e333972 100644
--- a/chrome/test/data/webui/tab_search/tab_search_test_helper.js
+++ b/chrome/test/data/webui/tab_search/tab_search_test_helper.js
@@ -94,13 +94,3 @@
 
   loadTimeData.overrideValues(loadTimeOverriddenData);
 }
-
-/**
- * @param {Object} object
- * @param {Object} Type
- * @returns {Object}
- */
-export function typed(object, Type) {
-  Object.setPrototypeOf(object, Type.prototype);
-  return object;
-}
diff --git a/chrome/updater/mac/setup/keystone.mm b/chrome/updater/mac/setup/keystone.mm
index 179d51ed..a088af1 100644
--- a/chrome/updater/mac/setup/keystone.mm
+++ b/chrome/updater/mac/setup/keystone.mm
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -100,6 +101,60 @@
   return true;
 }
 
+bool CreateEmptyFileInDirectory(const base::FilePath& dir,
+                                const std::string& file_name) {
+  constexpr int kPermissionsMask = base::FILE_PERMISSION_READ_BY_USER |
+                                   base::FILE_PERMISSION_WRITE_BY_USER |
+                                   base::FILE_PERMISSION_READ_BY_GROUP |
+                                   base::FILE_PERMISSION_READ_BY_OTHERS;
+
+  if (!base::PathExists(dir)) {
+    base::File::Error error;
+    if (!base::CreateDirectoryAndGetError(dir, &error) ||
+        !base::SetPosixFilePermissions(dir, kPermissionsMask)) {
+      LOG(ERROR) << "Failed to create '" << dir.value().c_str()
+                 << "': " << base::File::ErrorToString(error);
+      return false;
+    }
+  }
+
+  base::FilePath file_path = dir.AppendASCII(file_name);
+  base::File file(file_path,
+                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+  file.Close();
+
+  if (!base::PathExists(file_path)) {
+    LOG(ERROR) << "Failed to create file: " << file_path.value().c_str();
+    return false;
+  }
+  if (!base::SetPosixFilePermissions(file_path, kPermissionsMask)) {
+    LOG(ERROR) << "Failed to set permissions: " << file_path.value().c_str();
+    return false;
+  }
+
+  return true;
+}
+
+bool CreateKeystoneLaunchCtlPlistFiles(UpdaterScope scope) {
+  // If not all Keystone launchctl plist files are present, Keystone installer
+  // will proceed regardless of the bundle state. The empty launchctl files
+  // created here are used to make legacy Keystone installer believe that a
+  // healthy newer version updater already exists and thus won't over-install.
+  if (scope == UpdaterScope::kSystem &&
+      !CreateEmptyFileInDirectory(
+          GetLibraryFolderPath(scope)->Append("LaunchDaemons"),
+          "com.google.keystone.daemon.plist")) {
+    return false;
+  }
+
+  base::FilePath launch_agent_dir =
+      GetLibraryFolderPath(scope)->Append("LaunchAgents");
+  return CreateEmptyFileInDirectory(launch_agent_dir,
+                                    "com.google.keystone.agent.plist") &&
+         CreateEmptyFileInDirectory(launch_agent_dir,
+                                    "com.google.keystone.xpcservice.plist");
+}
+
 void MigrateKeystoneTickets(
     UpdaterScope scope,
     base::RepeatingCallback<void(const RegistrationRequest&)>
@@ -193,7 +248,8 @@
   MigrateKeystoneTickets(scope, register_callback);
   // TODO(crbug.com/1250524): Flush prefs, then delete the tickets to mitigate
   // duplicate imports.
-  return CopyKeystoneBundle(scope);
+
+  return CopyKeystoneBundle(scope) && CreateKeystoneLaunchCtlPlistFiles(scope);
 }
 
 }  // namespace updater
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 3231aaa..ac894f8 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14382.0.0
+14385.0.0
diff --git a/chromeos/dbus/power/power_policy_controller.cc b/chromeos/dbus/power/power_policy_controller.cc
index 1e37dfa..a4d8bfb 100644
--- a/chromeos/dbus/power/power_policy_controller.cc
+++ b/chromeos/dbus/power/power_policy_controller.cc
@@ -42,6 +42,7 @@
 #define APPEND_DELAYS(str, delays, prefix)                                 \
   {                                                                        \
     APPEND_DELAY(str, delays, screen_dim_ms, prefix "_screen_dim_ms");     \
+    APPEND_DELAY(str, delays, quick_dim_ms, prefix "_quick_dim_ms");       \
     APPEND_DELAY(str, delays, screen_off_ms, prefix "_screen_off_ms");     \
     APPEND_DELAY(str, delays, screen_lock_ms, prefix "_screen_lock_ms");   \
     APPEND_DELAY(str, delays, idle_warning_ms, prefix "_idle_warning_ms"); \
@@ -351,6 +352,11 @@
     StringAppendF(&str, "usb_power_share=%d ", policy.usb_power_share());
   }
 
+  if (policy.has_send_feedback_if_undimmed()) {
+    StringAppendF(&str, "send_feedback_if_undimmed=%d ",
+                  policy.send_feedback_if_undimmed());
+  }
+
   if (policy.has_reason())
     StringAppendF(&str, "reason=\"%s\" ", policy.reason().c_str());
   base::TrimWhitespaceASCII(str, base::TRIM_TRAILING, &str);
@@ -455,6 +461,20 @@
   delays->set_idle_warning_ms(values.battery_idle_warning_delay_ms);
   delays->set_idle_ms(values.battery_idle_delay_ms);
 
+  // Sets quick_dim_ms and send_feedback_if_undimmed for prefs_policy_.
+  if (values.battery_quick_dim_delay_ms > 0) {
+    prefs_policy_.mutable_battery_delays()->set_quick_dim_ms(
+        values.battery_quick_dim_delay_ms);
+  }
+  if (values.ac_quick_dim_delay_ms > 0) {
+    prefs_policy_.mutable_ac_delays()->set_quick_dim_ms(
+        values.ac_quick_dim_delay_ms);
+  }
+  if (values.send_feedback_if_undimmed.has_value()) {
+    prefs_policy_.set_send_feedback_if_undimmed(
+        values.send_feedback_if_undimmed.value());
+  }
+
   lock_ms = delays->screen_off_ms() + kScreenLockAfterOffDelayMs;
   if (values.enable_auto_screen_lock && delays->screen_off_ms() > 0 &&
       (delays->screen_lock_ms() <= 0 || lock_ms < delays->screen_lock_ms()) &&
diff --git a/chromeos/dbus/power/power_policy_controller.h b/chromeos/dbus/power/power_policy_controller.h
index 6645a814..1531f74 100644
--- a/chromeos/dbus/power/power_policy_controller.h
+++ b/chromeos/dbus/power/power_policy_controller.h
@@ -13,6 +13,7 @@
 #include "base/values.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/policy.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
 
@@ -70,11 +71,13 @@
     // the other fields, unfortunately (but the default values would only reach
     // powerd if Chrome failed to override them with the pref-assigned values).
     int ac_screen_dim_delay_ms = -1;
+    int ac_quick_dim_delay_ms = -1;
     int ac_screen_off_delay_ms = -1;
     int ac_screen_lock_delay_ms = -1;
     int ac_idle_warning_delay_ms = -1;
     int ac_idle_delay_ms = -1;
     int battery_screen_dim_delay_ms = -1;
+    int battery_quick_dim_delay_ms = -1;
     int battery_screen_off_delay_ms = -1;
     int battery_screen_lock_delay_ms = -1;
     int battery_idle_warning_delay_ms = -1;
@@ -109,6 +112,7 @@
             power_manager::PowerManagementPolicy::BatteryChargeMode::ADAPTIVE;
     int custom_charge_start = -1;
     int custom_charge_stop = -1;
+    absl::optional<bool> send_feedback_if_undimmed;
   };
 
   // Converts |base::DictionaryValue| to |std::vector<PeakShiftDayConfig>| and
diff --git a/chromeos/dbus/power/power_policy_controller_unittest.cc b/chromeos/dbus/power/power_policy_controller_unittest.cc
index ff5c1f4..96ec2e5 100644
--- a/chromeos/dbus/power/power_policy_controller_unittest.cc
+++ b/chromeos/dbus/power/power_policy_controller_unittest.cc
@@ -49,9 +49,11 @@
 TEST_F(PowerPolicyControllerTest, Prefs) {
   PowerPolicyController::PrefValues prefs;
   prefs.ac_screen_dim_delay_ms = 600000;
+  prefs.ac_quick_dim_delay_ms = 500000;
   prefs.ac_screen_off_delay_ms = 660000;
   prefs.ac_idle_delay_ms = 720000;
   prefs.battery_screen_dim_delay_ms = 300000;
+  prefs.battery_quick_dim_delay_ms = 250000;
   prefs.battery_screen_off_delay_ms = 360000;
   prefs.battery_idle_delay_ms = 420000;
   prefs.ac_idle_action = PowerPolicyController::ACTION_SUSPEND;
@@ -68,15 +70,18 @@
   prefs.force_nonzero_brightness_for_user_activity = false;
   prefs.boot_on_ac = true;
   prefs.usb_power_share = false;
+  prefs.send_feedback_if_undimmed = true;
   policy_controller_->ApplyPrefs(prefs);
 
   power_manager::PowerManagementPolicy expected_policy;
   expected_policy.mutable_ac_delays()->set_screen_dim_ms(600000);
+  expected_policy.mutable_ac_delays()->set_quick_dim_ms(500000);
   expected_policy.mutable_ac_delays()->set_screen_off_ms(660000);
   expected_policy.mutable_ac_delays()->set_screen_lock_ms(-1);
   expected_policy.mutable_ac_delays()->set_idle_warning_ms(-1);
   expected_policy.mutable_ac_delays()->set_idle_ms(720000);
   expected_policy.mutable_battery_delays()->set_screen_dim_ms(300000);
+  expected_policy.mutable_battery_delays()->set_quick_dim_ms(250000);
   expected_policy.mutable_battery_delays()->set_screen_off_ms(360000);
   expected_policy.mutable_battery_delays()->set_screen_lock_ms(-1);
   expected_policy.mutable_battery_delays()->set_idle_warning_ms(-1);
@@ -97,6 +102,7 @@
   expected_policy.set_force_nonzero_brightness_for_user_activity(false);
   expected_policy.set_boot_on_ac(true);
   expected_policy.set_usb_power_share(false);
+  expected_policy.set_send_feedback_if_undimmed(true);
   expected_policy.mutable_battery_charge_mode()->set_mode(
       power_manager::PowerManagementPolicy::BatteryChargeMode::ADAPTIVE);
 
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
index 698fa88c..20ff33c 100644
--- a/chromeos/network/device_state.cc
+++ b/chromeos/network/device_state.cc
@@ -122,7 +122,7 @@
     // If kIPConfigsProperty changes, clear any previous ip_configs_.
     // ShillPropertyhandler will request the IPConfig objects which will trigger
     // calls to IPConfigPropertiesChanged.
-    ip_configs_.Clear();
+    ip_configs_.DictClear();
     return false;  // No actual state change.
   } else if (key == shill::kLinkUpProperty) {
     return GetBooleanValue(key, value, &link_up_);
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
index dcf4b633..9cf929c 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
@@ -806,12 +806,10 @@
 
     @CalledByNative
     private void onQueryDone(boolean success) {
-        if (mRequest != null) {
-            mRequest.onQueryDone(success);
-        }
+        if (mRequest == null) return;
+        mRequest.onQueryDone(success);
         mAutofillUMA.onServerTypeAvailable(
-                (success && mRequest != null) ? mRequest.mFormData : null,
-                /*afterSessionStarted*/ true);
+                success ? mRequest.mFormData : null, /*afterSessionStarted*/ true);
         mAutofillManager.onQueryDone(success);
     }
 
diff --git a/components/breadcrumbs/core/BUILD.gn b/components/breadcrumbs/core/BUILD.gn
index ea6202f..85020c8b 100644
--- a/components/breadcrumbs/core/BUILD.gn
+++ b/components/breadcrumbs/core/BUILD.gn
@@ -60,6 +60,7 @@
     "breadcrumb_manager_keyed_service_unittest.cc",
     "breadcrumb_manager_observer_unittest.cc",
     "breadcrumb_manager_unittest.cc",
+    "breadcrumb_persistent_storage_manager_unittest.cc",
     "breadcrumb_persistent_storage_util_unittest.cc",
   ]
 }
diff --git a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc
new file mode 100644
index 0000000..5374ce91
--- /dev/null
+++ b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc
@@ -0,0 +1,274 @@
+// 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 "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/task_environment.h"
+#include "components/breadcrumbs/core/breadcrumb_manager_keyed_service.h"
+#include "components/breadcrumbs/core/breadcrumb_persistent_storage_util.h"
+#include "testing/platform_test.h"
+
+namespace breadcrumbs {
+
+namespace {
+
+// Estimate number of events too large to fit in the persisted file. 6 is based
+// on the event string format which is slightly smaller than each event.
+constexpr unsigned long kEventCountTooManyForPersisting =
+    kPersistedFilesizeInBytes / 6.0;
+
+// Validates that the events in `persisted_events` are contiguous and that the
+// `last_logged_event` matches the last persisted event.
+bool ValidatePersistedEvents(const std::string& last_logged_event,
+                             std::vector<std::string> persisted_events) {
+  if (persisted_events.empty())
+    return false;
+
+  // The newest event is at the back of the vector.
+  const std::string last_event = persisted_events.back();
+  if (last_event.find(last_logged_event) == std::string::npos)
+    return false;
+
+  // The oldest event is at the front of the vector.
+  const std::string first_event = persisted_events.front();
+  int first_event_index;
+  if (!base::StringToInt(first_event.substr(first_event.find_last_of(' ') + 1),
+                         &first_event_index)) {
+    // `first_event_index` could not be parsed.
+    return false;
+  }
+
+  int last_event_index;
+  if (!base::StringToInt(last_event.substr(last_event.find_last_of(' ') + 1),
+                         &last_event_index)) {
+    // `last_event_index` could not be parsed.
+    return false;
+  }
+
+  // Validate no events are missing from within the persisted range.
+  return last_event_index - first_event_index + 1 ==
+         static_cast<int>(persisted_events.size());
+}
+
+// Creates and returns a new file at `path`, overwriting any existing file.
+base::File CreateFile(const base::FilePath& path) {
+  constexpr int kFlags =
+      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
+  return base::File(path, kFlags);
+}
+
+}  // namespace
+
+class BreadcrumbPersistentStorageManagerTest : public PlatformTest {
+ protected:
+  BreadcrumbPersistentStorageManagerTest() {
+    EXPECT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+    persistent_storage_ = std::make_unique<BreadcrumbPersistentStorageManager>(
+        scoped_temp_dir_.GetPath());
+    breadcrumb_manager_service_.StartPersisting(persistent_storage_.get());
+  }
+
+  ~BreadcrumbPersistentStorageManagerTest() override {
+    breadcrumb_manager_service_.StopPersisting();
+  }
+
+  // Calls `GetStoredEvents()` and wait for its posted tasks to complete.
+  std::vector<std::string> GetPersistedEvents() {
+    base::RunLoop run_loop;
+    persistent_storage_->GetStoredEvents(
+        base::BindOnce(&BreadcrumbPersistentStorageManagerTest::WaitForEvents,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+    return events_;
+  }
+
+  // Run `quit_closure` to signal to the run loop that the function being tested
+  // has completed, and sets `events_` to the `events` vector received from the
+  // function being tested (so that tests may make assertions about the events).
+  void WaitForEvents(base::OnceClosure quit_closure,
+                     std::vector<std::string> events) {
+    events_.clear();
+    events_ = events;
+    std::move(quit_closure).Run();
+  }
+
+  base::test::TaskEnvironment task_env_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  base::ScopedTempDir scoped_temp_dir_;
+  BreadcrumbManagerKeyedService breadcrumb_manager_service_{
+      /*is_off_the_record=*/false};
+  std::unique_ptr<BreadcrumbPersistentStorageManager> persistent_storage_;
+
+  // Stores events returned by BreadcrumbPersistentStorageManager::GetEvents().
+  std::vector<std::string> events_;
+};
+
+// Ensures that logged events are persisted.
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistEvents) {
+  breadcrumb_manager_service_.AddEvent("event");
+
+  // Advance clock to trigger writing final events.
+  task_env_.FastForwardBy(base::Minutes(1));
+
+  const auto events = GetPersistedEvents();
+  ASSERT_EQ(1ul, events.size());
+  EXPECT_NE(std::string::npos, events.front().find("event"));
+}
+
+// Ensures that persisted events do not grow too large for a single large
+// event bucket when events are logged very quickly one after the other.
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistLargeBucket) {
+  std::string event;
+  unsigned long event_count = 0;
+  while (event_count < kEventCountTooManyForPersisting) {
+    event = base::StringPrintf("event %lu", event_count);
+    breadcrumb_manager_service_.AddEvent(event);
+    task_env_.FastForwardBy(base::Milliseconds(1));
+
+    event_count++;
+  }
+
+  // Advance clock to trigger writing final events.
+  task_env_.FastForwardBy(base::Minutes(1));
+
+  const auto events = GetPersistedEvents();
+  EXPECT_LT(events.size(), event_count);
+  EXPECT_TRUE(ValidatePersistedEvents(event, events));
+}
+
+// Ensures that persisted events do not grow too large for events logged a few
+// seconds apart from each other.
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistManyEventsOverTime) {
+  std::string event;
+  unsigned long event_count = 0;
+  while (event_count < kEventCountTooManyForPersisting) {
+    event = base::StringPrintf("event %lu", event_count);
+    breadcrumb_manager_service_.AddEvent(event);
+    task_env_.FastForwardBy(base::Seconds(1));
+
+    event_count++;
+  }
+
+  // Advance clock to trigger writing final events.
+  task_env_.FastForwardBy(base::Minutes(1));
+
+  const auto events = GetPersistedEvents();
+  ASSERT_GT(events.size(), 0ul);
+  EXPECT_LT(events.size(), event_count);
+  EXPECT_TRUE(ValidatePersistedEvents(event, events));
+}
+
+// Ensures that old events are removed from the persisted file when old buckets
+// are dropped.
+TEST_F(BreadcrumbPersistentStorageManagerTest,
+       OldEventsRemovedFromPersistedFile) {
+  std::string event;
+  unsigned long event_counter = 0;
+  constexpr int kNumEventsPerBucket = 200;
+  constexpr int kNumEvents = kNumEventsPerBucket * 3;
+  while (event_counter < kNumEvents) {
+    event = base::StringPrintf("event %lu", event_counter);
+    breadcrumb_manager_service_.AddEvent(event);
+    event_counter++;
+
+    if (event_counter % kNumEventsPerBucket == 0)
+      task_env_.FastForwardBy(base::Hours(1));
+  }
+
+  // Advance clock to trigger writing final events.
+  task_env_.FastForwardBy(base::Minutes(1));
+
+  // The exact number of events could vary based on changes in the
+  // implementation. The important part of this test is to verify that a single
+  // event bucket will not grow unbounded and it will be limited to a value
+  // smaller than the overall total number of events which have been logged.
+  const auto events = GetPersistedEvents();
+  EXPECT_LT(events.size(), event_counter);
+  EXPECT_TRUE(ValidatePersistedEvents(event, events));
+}
+
+// Ensures that events are read correctly if the persisted file becomes
+// corrupted by losing the EOF token or if kPersistedFilesizeInBytes is reduced.
+TEST_F(BreadcrumbPersistentStorageManagerTest,
+       GetStoredEventsAfterFilesizeReduction) {
+  const base::FilePath breadcrumbs_file_path =
+      GetBreadcrumbPersistentStorageFilePath(scoped_temp_dir_.GetPath());
+  base::File file = CreateFile(breadcrumbs_file_path);
+  ASSERT_TRUE(file.IsValid());
+
+  // Simulate an old persisted file larger than the current one.
+  const size_t old_filesize = kPersistedFilesizeInBytes * 1.2;
+  std::string past_breadcrumbs;
+  unsigned long written_events = 0;
+  while (past_breadcrumbs.length() < old_filesize) {
+    past_breadcrumbs += "08:27 event\n";
+    written_events++;
+  }
+
+  ASSERT_TRUE(file.WriteAndCheck(
+      /*offset=*/0, base::as_bytes(base::make_span(past_breadcrumbs))));
+  ASSERT_TRUE(file.Flush());
+  file.Close();
+
+  const auto events = GetPersistedEvents();
+  EXPECT_GT(events.size(), 1ul);
+  EXPECT_LT(events.size(), written_events);
+}
+
+using BreadcrumbPersistentStorageManagerFilenameTest = PlatformTest;
+
+TEST_F(BreadcrumbPersistentStorageManagerFilenameTest,
+       MigrateOldBreadcrumbFiles) {
+  base::test::TaskEnvironment task_env;
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+  const base::FilePath scoped_temp_dir_path = scoped_temp_dir.GetPath();
+
+  // Create breadcrumb file and temp file with old filenames.
+  const base::FilePath old_breadcrumb_file_path =
+      scoped_temp_dir_path.Append(FILE_PATH_LITERAL("old breadcrumb file"));
+  const base::FilePath old_temp_file_path =
+      scoped_temp_dir_path.Append(FILE_PATH_LITERAL("old temp file"));
+  base::File old_breadcrumb_file = CreateFile(old_breadcrumb_file_path);
+  base::File old_temp_file = CreateFile(old_temp_file_path);
+  ASSERT_TRUE(old_breadcrumb_file.IsValid());
+  ASSERT_TRUE(old_temp_file.IsValid());
+  old_temp_file.Close();
+
+  // Write some test data to the breadcrumb file.
+  const std::string test_data = "breadcrumb file test data";
+  ASSERT_NE(-1,
+            old_breadcrumb_file.Write(0, test_data.c_str(), test_data.size()));
+  old_breadcrumb_file.Close();
+
+  BreadcrumbPersistentStorageManager persistent_storage(
+      scoped_temp_dir_path, old_breadcrumb_file_path, old_temp_file_path);
+  task_env.RunUntilIdle();
+
+  // The old files should have been removed, and the new breadcrumb file
+  // should be present.
+  EXPECT_FALSE(base::PathExists(old_breadcrumb_file_path));
+  EXPECT_FALSE(base::PathExists(old_temp_file_path));
+  base::File new_file(
+      GetBreadcrumbPersistentStorageFilePath(scoped_temp_dir_path),
+      base::File::FLAG_OPEN | base::File::FLAG_READ);
+  EXPECT_TRUE(new_file.IsValid());
+  const size_t test_data_size = test_data.size();
+  char new_file_data[test_data_size];
+  EXPECT_EQ(static_cast<int>(test_data_size),
+            new_file.Read(0, new_file_data, test_data_size));
+  EXPECT_EQ(test_data, std::string(new_file_data, test_data_size));
+}
+
+}  // namespace breadcrumbs
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 0ee7ec8c..d31b092 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -105,12 +105,19 @@
 
 }  // namespace
 
+OperatorHistoryEntry::OperatorHistoryEntry() = default;
+OperatorHistoryEntry::~OperatorHistoryEntry() = default;
+OperatorHistoryEntry::OperatorHistoryEntry(const OperatorHistoryEntry& other) =
+    default;
+
 ChromeCTPolicyEnforcer::ChromeCTPolicyEnforcer(
     base::Time log_list_date,
     std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs,
-    std::vector<std::string> operated_by_google_logs)
-    : disqualified_logs_(disqualified_logs),
-      operated_by_google_logs_(operated_by_google_logs),
+    std::vector<std::string> operated_by_google_logs,
+    std::map<std::string, OperatorHistoryEntry> log_operator_history)
+    : disqualified_logs_(std::move(disqualified_logs)),
+      operated_by_google_logs_(std::move(operated_by_google_logs)),
+      log_operator_history_(std::move(log_operator_history)),
       clock_(base::DefaultClock::GetInstance()),
       log_list_date_(log_list_date) {}
 
@@ -144,10 +151,12 @@
 void ChromeCTPolicyEnforcer::UpdateCTLogList(
     base::Time update_time,
     std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs,
-    std::vector<std::string> operated_by_google_logs) {
+    std::vector<std::string> operated_by_google_logs,
+    std::map<std::string, OperatorHistoryEntry> log_operator_history) {
   log_list_date_ = update_time;
   disqualified_logs_ = std::move(disqualified_logs);
   operated_by_google_logs_ = std::move(operated_by_google_logs);
+  log_operator_history_ = std::move(log_operator_history);
 }
 
 bool ChromeCTPolicyEnforcer::IsLogDisqualified(
@@ -341,4 +350,18 @@
              : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
 }
 
+std::string ChromeCTPolicyEnforcer::GetOperatorForLog(
+    std::string log_id,
+    base::TimeDelta timestamp) {
+  DCHECK(log_operator_history_.find(log_id) != log_operator_history_.end());
+  OperatorHistoryEntry log_history = log_operator_history_[log_id];
+  for (auto operator_entry : log_history.previous_operators_) {
+    if (timestamp < operator_entry.second)
+      return operator_entry.first;
+  }
+  // Either the log has only ever had one operator, or the timestamp is after
+  // the last operator change.
+  return log_history.current_operator_;
+}
+
 }  // namespace certificate_transparency
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.h b/components/certificate_transparency/chrome_ct_policy_enforcer.h
index 1ee18c1..234910e9 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.h
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_CERTIFICATE_TRANSPARENCY_CHROME_CT_POLICY_ENFORCER_H_
 #define COMPONENTS_CERTIFICATE_TRANSPARENCY_CHROME_CT_POLICY_ENFORCER_H_
 
+#include <map>
 #include <string>
 #include <utility>
 #include <vector>
@@ -16,6 +17,18 @@
 
 namespace certificate_transparency {
 
+struct OperatorHistoryEntry {
+  // Name of the current operator for the log.
+  std::string current_operator_;
+  // Vector of previous operators (if any) for the log, represented as pairs of
+  // operator name and time when they stopped operating the log.
+  std::vector<std::pair<std::string, base::TimeDelta>> previous_operators_;
+
+  OperatorHistoryEntry();
+  ~OperatorHistoryEntry();
+  OperatorHistoryEntry(const OperatorHistoryEntry& other);
+};
+
 // A CTPolicyEnforcer that enforces the "Certificate Transparency in Chrome"
 // policies detailed at
 // https://github.com/chromium/ct-policy/blob/master/ct_policy.md
@@ -35,7 +48,8 @@
   ChromeCTPolicyEnforcer(
       base::Time log_list_date,
       std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs,
-      std::vector<std::string> operated_by_google_logs);
+      std::vector<std::string> operated_by_google_logs,
+      std::map<std::string, OperatorHistoryEntry> log_operator_history);
 
   ~ChromeCTPolicyEnforcer() override;
 
@@ -50,7 +64,8 @@
   void UpdateCTLogList(
       base::Time update_time,
       std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs,
-      std::vector<std::string> operated_by_google_logs);
+      std::vector<std::string> operated_by_google_logs,
+      std::map<std::string, OperatorHistoryEntry> log_operator_history);
 
   void SetClockForTesting(const base::Clock* clock) { clock_ = clock; }
 
@@ -67,6 +82,11 @@
     return disqualified_logs_;
   }
 
+  const std::map<std::string, OperatorHistoryEntry>&
+  operator_history_for_testing() {
+    return log_operator_history_;
+  }
+
  private:
   // Returns true if the log identified by |log_id| (the SHA-256 hash of the
   // log's DER-encoded SPKI) has been disqualified, and sets
@@ -87,12 +107,16 @@
       const net::X509Certificate& cert,
       const net::ct::SCTList& verified_scts) const;
 
+  std::string GetOperatorForLog(std::string log_id, base::TimeDelta timestamp);
+
   // Map of SHA-256(SPKI) to log disqualification date.
   std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs_;
 
   // List of SHA-256(SPKI) for logs operated by Google.
   std::vector<std::string> operated_by_google_logs_;
 
+  std::map<std::string, OperatorHistoryEntry> log_operator_history_;
+
   raw_ptr<const base::Clock> clock_;
 
   // The time at which |disqualified_logs_| and |operated_by_google_logs_| were
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index a8f783c..49dee17 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -46,7 +46,8 @@
  public:
   void SetUp() override {
     auto enforcer = std::make_unique<ChromeCTPolicyEnforcer>(
-        base::Time::Now(), GetDisqualifiedLogs(), GetLogsOperatedByGoogle());
+        base::Time::Now(), GetDisqualifiedLogs(), GetLogsOperatedByGoogle(),
+        std::map<std::string, OperatorHistoryEntry>());
     enforcer->SetClockForTesting(&clock_);
     policy_enforcer_ = std::move(enforcer);
 
@@ -481,7 +482,8 @@
   std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
   chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs);
+                                          operated_by_google_logs,
+                                          /*log_operator_history=*/{});
 
   // The check should fail since the Google Aviator log is no longer in the
   // list after the update with an empty list.
@@ -493,7 +495,8 @@
   // logs.
   operated_by_google_logs = certificate_transparency::GetLogsOperatedByGoogle();
   chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs);
+                                          operated_by_google_logs,
+                                          /*log_operator_history=*/{});
 
   // The check should now succeed.
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
@@ -511,9 +514,10 @@
   // Clear the log list and set the last updated time to more than 10 weeks ago.
   std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
-  chrome_policy_enforcer->UpdateCTLogList(base::Time::Now() - base::Days(71),
-                                          disqualified_logs,
-                                          operated_by_google_logs);
+  std::map<std::string, OperatorHistoryEntry> log_operator_history;
+  chrome_policy_enforcer->UpdateCTLogList(
+      base::Time::Now() - base::Days(71), disqualified_logs,
+      operated_by_google_logs, log_operator_history);
 
   // The check should return build not timely even though the Google Aviator log
   // is no longer in the list, since the last update time is greater than 10
@@ -524,7 +528,8 @@
 
   // Update the last update time value again, this time with a recent time.
   chrome_policy_enforcer->UpdateCTLogList(base::Time::Now(), disqualified_logs,
-                                          operated_by_google_logs);
+                                          operated_by_google_logs,
+                                          log_operator_history);
 
   // The check should now fail
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
diff --git a/components/cronet/PRESUBMIT.py b/components/cronet/PRESUBMIT.py
index 6558305..62f1fc47 100644
--- a/components/cronet/PRESUBMIT.py
+++ b/components/cronet/PRESUBMIT.py
@@ -10,6 +10,9 @@
 
 import os
 
+USE_PYTHON3 = True
+
+
 def _PyLintChecks(input_api, output_api):
   pylint_checks = input_api.canned_checks.GetPylint(input_api, output_api,
           extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc')
diff --git a/components/cronet/android/test/javaperftests/run.py b/components/cronet/android/test/javaperftests/run.py
index 440df33..bbbace4 100755
--- a/components/cronet/android/test/javaperftests/run.py
+++ b/components/cronet/android/test/javaperftests/run.py
@@ -47,7 +47,7 @@
 import sys
 import tempfile
 import time
-import urllib
+import six.moves.urllib_parse # pylint: disable=import-error
 
 REPOSITORY_ROOT = os.path.abspath(os.path.join(
     os.path.dirname(__file__), '..', '..', '..', '..', '..'))
@@ -87,7 +87,7 @@
     self._device = device
     config = perf_test_utils.GetConfig(device)
     device.RemovePath(config['DONE_FILE'], force=True)
-    self.url ='http://dummy/?'+urllib.urlencode(config)
+    self.url ='http://dummy/?' + six.moves.urllib_parse.urlencode(config)
     start_intent = intent.Intent(
         package=perf_test_utils.APP_PACKAGE,
         activity=perf_test_utils.APP_ACTIVITY,
diff --git a/components/cronet/tools/api_static_checks.py b/components/cronet/tools/api_static_checks.py
index 9c78434..5c8bc77 100755
--- a/components/cronet/tools/api_static_checks.py
+++ b/components/cronet/tools/api_static_checks.py
@@ -5,7 +5,7 @@
 
 """api_static_checks.py - Enforce Cronet API requirements."""
 
-from __future__ import print_function
+
 
 import argparse
 import os
diff --git a/components/cronet/tools/api_static_checks_unittest.py b/components/cronet/tools/api_static_checks_unittest.py
index 7f415dcf..1ca46ec5 100755
--- a/components/cronet/tools/api_static_checks_unittest.py
+++ b/components/cronet/tools/api_static_checks_unittest.py
@@ -7,13 +7,13 @@
 
 
 import contextlib
-from cStringIO import StringIO
-import md5
+import hashlib
 import os
 import shutil
 import sys
 import tempfile
 import unittest
+import six
 
 REPOSITORY_ROOT = os.path.abspath(os.path.join(
     os.path.dirname(__file__), '..', '..', '..'))
@@ -58,7 +58,7 @@
 
   oldout,olderr = sys.stdout, sys.stderr
   try:
-    out=[StringIO(), StringIO()]
+    out=[six.StringIO(), six.StringIO()]
     sys.stdout,sys.stderr = out
     yield out
   finally:
@@ -147,9 +147,9 @@
     api_stamp = api.split('\n')[-2]
     stamp_length = len('Stamp: 78418460c193047980ae9eabb79293f2\n')
     api = api[:-stamp_length]
-    api_hash = md5.new()
+    api_hash = hashlib.md5()
     api_hash.update(api)
-    self.assertEquals(api_stamp, 'Stamp: %s' % api_hash.hexdigest())
+    self.assertEqual(api_stamp, 'Stamp: %s' % api_hash.hexdigest())
 
     return [return_code == 0, output, api, api_version]
 
diff --git a/components/cronet/tools/cr_cronet.py b/components/cronet/tools/cr_cronet.py
index 8d7927a..99485859 100755
--- a/components/cronet/tools/cr_cronet.py
+++ b/components/cronet/tools/cr_cronet.py
@@ -19,13 +19,13 @@
 
 
 def run(command, **kwargs):
-  print command, kwargs
+  print(command, kwargs)
   return subprocess.call(command, **kwargs)
 
 
 def run_shell(command, extra_options=''):
   command = command + ' ' + extra_options
-  print command
+  print(command)
   return os.system(command)
 
 
@@ -121,7 +121,7 @@
 
 
 def get_ios_gn_args(is_release, bundle_id_prefix, target_cpu):
-  print is_release, bundle_id_prefix, target_cpu
+  print(is_release, bundle_id_prefix, target_cpu)
   return get_mobile_gn_args('ios', is_release) + \
       ('is_cronet_build=true  '
       'enable_remoting=false '
@@ -179,8 +179,8 @@
                       help='configure bundle id prefix')
 
   options, extra_options = parser.parse_known_args()
-  print options
-  print extra_options
+  print(options)
+  print(extra_options)
 
   if is_ios:
     test_target = 'cronet_test'
diff --git a/components/cronet/tools/generate_idl_bindings.py b/components/cronet/tools/generate_idl_bindings.py
index 4df9c7dd..93599da2 100755
--- a/components/cronet/tools/generate_idl_bindings.py
+++ b/components/cronet/tools/generate_idl_bindings.py
@@ -12,7 +12,7 @@
 
 def run(command, extra_options=''):
   command = command + ' ' + extra_options
-  print command
+  print(command)
   ret = os.system(command)
   if ret != 0:
     raise OSError(ret)
diff --git a/components/cronet/tools/generators/cronet_bindings_generator.py b/components/cronet/tools/generators/cronet_bindings_generator.py
index 02cff017..9e880c5 100755
--- a/components/cronet/tools/generators/cronet_bindings_generator.py
+++ b/components/cronet/tools/generators/cronet_bindings_generator.py
@@ -63,7 +63,7 @@
       generator_name = os.path.join(script_dir,
                                     _BUILTIN_GENERATORS[language])
     else:
-      print "Unknown generator name %s" % generator_name
+      print("Unknown generator name %s" % generator_name)
       sys.exit(1)
     generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
                                        generator_name)
@@ -161,7 +161,7 @@
       with open(filename) as f:
         typemaps = json.loads("".join([l for l in f.readlines()
                                        if no_comments(l)]))
-        for language, typemap in typemaps.iteritems():
+        for language, typemap in typemaps.items():
           language_map = self._typemap.get(language, {})
           language_map.update(typemap)
           self._typemap[language] = language_map
@@ -205,7 +205,7 @@
 
     if self._should_generate(rel_filename.path):
       AddComputedData(module)
-      for language, generator_module in generator_modules.iteritems():
+      for language, generator_module in generator_modules.items():
         generator = generator_module.Generator(
             module, args.output_dir, typemap=self._typemap.get(language, {}),
             variant=args.variant, bytecode_path=args.bytecode_path,
@@ -232,23 +232,23 @@
       return
 
     if rel_filename.path in imported_filename_stack:
-      print "%s: Error: Circular dependency" % rel_filename.path + \
-          MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+      print("%s: Error: Circular dependency" % rel_filename.path + \
+          MakeImportStackMessage(imported_filename_stack + [rel_filename.path]))
       sys.exit(1)
 
     try:
       with open(rel_filename.path) as f:
         source = f.read()
     except IOError as e:
-      print "%s: Error: %s" % (rel_filename.path, e.strerror) + \
-          MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
+      print("%s: Error: %s" % (rel_filename.path, e.strerror) + \
+          MakeImportStackMessage(imported_filename_stack + [rel_filename.path]))
       sys.exit(1)
 
     try:
       tree = Parse(source, rel_filename.path)
     except Error as e:
       full_stack = imported_filename_stack + [rel_filename.path]
-      print str(e) + MakeImportStackMessage(full_stack)
+      print(str(e) + MakeImportStackMessage(full_stack))
       sys.exit(1)
 
     dirname = os.path.split(rel_filename.path)[0]
@@ -285,13 +285,13 @@
     with open(args.depfile, 'w') as f:
       f.write('%s: %s' % (
           args.depfile_target,
-          ' '.join(processor._parsed_files.keys())))
+          ' '.join(list(processor._parsed_files.keys()))))
 
   return 0
 
 
 def _Precompile(args, _):
-  generator_modules = LoadGenerators(",".join(_BUILTIN_GENERATORS.keys()))
+  generator_modules = LoadGenerators(",".join(list(_BUILTIN_GENERATORS.keys())))
 
   template_expander.PrecompileTemplates(generator_modules, args.output_dir)
   return 0
diff --git a/components/cronet/tools/generators/cronet_c_generator.py b/components/cronet/tools/generators/cronet_c_generator.py
index 7fe4d753..3c80f08 100644
--- a/components/cronet/tools/generators/cronet_c_generator.py
+++ b/components/cronet/tools/generators/cronet_c_generator.py
@@ -275,7 +275,7 @@
                 for typename in
                 self.module.structs + all_enums + self.module.unions)
     headers = set()
-    for typename, typemap in self.typemap.iteritems():
+    for typename, typemap in self.typemap.items():
       if typename in types:
         headers.update(typemap.get("public_headers", []))
     return sorted(headers)
diff --git a/components/cronet/tools/hide_symbols.py b/components/cronet/tools/hide_symbols.py
index 374982c1..8bc50c3 100755
--- a/components/cronet/tools/hide_symbols.py
+++ b/components/cronet/tools/hide_symbols.py
@@ -12,7 +12,7 @@
 # This way, we can reduce risk of symbol conflict when linking it into apps
 # by exposing internal symbols, especially in third-party libraries.
 
-from __future__ import print_function
+
 
 import glob
 import optparse
diff --git a/components/cronet/tools/link_dependencies.py b/components/cronet/tools/link_dependencies.py
index 94e612e9..bd25c0c 100755
--- a/components/cronet/tools/link_dependencies.py
+++ b/components/cronet/tools/link_dependencies.py
@@ -135,7 +135,7 @@
   _, err = p.communicate()
   for line in err.splitlines():
     if not libtool_re.match(line):
-      print >>sys.stderr, line
+      sys.stderr.write(line)
   if p.returncode != 0:
     message = "subprocess libtool returned {0}".format(p.returncode)
     raise SubprocessError(message)
diff --git a/components/cronet/tools/update_api.py b/components/cronet/tools/update_api.py
index c725debe..8dff75d 100755
--- a/components/cronet/tools/update_api.py
+++ b/components/cronet/tools/update_api.py
@@ -5,7 +5,7 @@
 
 """update_api.py - Update committed Cronet API."""
 
-from __future__ import print_function
+
 
 import argparse
 import filecmp
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index f0146fca..9d8538b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -129,7 +129,7 @@
   base::DictionaryValue* pref_dict_dst = pref_update_dst.Get();
   DictionaryPrefUpdate pref_update_src(pref_service, pref_src);
   base::DictionaryValue* pref_dict_src = pref_update_src.Get();
-  pref_dict_dst->Clear();
+  pref_dict_dst->DictClear();
   pref_dict_dst->Swap(pref_dict_src);
   DCHECK(pref_dict_src->DictEmpty());
 }
diff --git a/components/desks_storage/BUILD.gn b/components/desks_storage/BUILD.gn
index 82c59cc8c..5702d517 100644
--- a/components/desks_storage/BUILD.gn
+++ b/components/desks_storage/BUILD.gn
@@ -27,6 +27,7 @@
     "//components/sync/protocol",
     "//components/version_info:channel",
     "//third_party/re2",
+    "//ui/gfx/geometry",
   ]
 
   defines = [ "IS_DESKS_STORAGE_IMPL" ]
diff --git a/components/desks_storage/core/desk_model.cc b/components/desks_storage/core/desk_model.cc
index 664364b..23f34894 100644
--- a/components/desks_storage/core/desk_model.cc
+++ b/components/desks_storage/core/desk_model.cc
@@ -6,12 +6,30 @@
 
 #include "ash/public/cpp/desk_template.h"
 #include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
 #include "components/desks_storage/core/desk_model_observer.h"
 #include "components/desks_storage/core/desk_template_conversion.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace desks_storage {
 
+namespace {
+DeskModel::GetTemplateJsonStatus ConvertGetEntryStatusToTemplateJsonStatus(
+    const DeskModel::GetEntryByUuidStatus status) {
+  switch (status) {
+    case DeskModel::GetEntryByUuidStatus::kOk:
+      return DeskModel::GetTemplateJsonStatus::kOk;
+    case DeskModel::GetEntryByUuidStatus::kFailure:
+      return DeskModel::GetTemplateJsonStatus::kFailure;
+    case DeskModel::GetEntryByUuidStatus::kNotFound:
+      return DeskModel::GetTemplateJsonStatus::kNotFound;
+    case DeskModel::GetEntryByUuidStatus::kInvalidUuid:
+      return DeskModel::GetTemplateJsonStatus::kInvalidUuid;
+  }
+}
+
+}  // namespace
+
 DeskModel::DeskModel() = default;
 
 DeskModel::~DeskModel() {
@@ -45,4 +63,36 @@
   }
 }
 
+void DeskModel::GetTemplateJson(const std::string& uuid,
+                                apps::AppRegistryCache* app_cache,
+                                GetTemplateJsonCallback callback) {
+  GetEntryByUUID(
+      uuid,
+      base::BindOnce(&DeskModel::HandleTemplateConversionToPolicyJson,
+                     base::Unretained(this), std::move(callback), app_cache));
+}
+
+void DeskModel::HandleTemplateConversionToPolicyJson(
+    GetTemplateJsonCallback callback,
+    apps::AppRegistryCache* app_cache,
+    GetEntryByUuidStatus status,
+    std::unique_ptr<ash::DeskTemplate> entry) {
+  if (status != GetEntryByUuidStatus::kOk) {
+    std::move(callback).Run(ConvertGetEntryStatusToTemplateJsonStatus(status),
+                            "");
+    return;
+  }
+
+  std::string raw_json;
+  bool conversion_success = base::JSONWriter::Write(
+      desk_template_conversion::SerializeDeskTemplateAsPolicy(entry.get(),
+                                                              app_cache),
+      &raw_json);
+
+  if (conversion_success)
+    std::move(callback).Run(GetTemplateJsonStatus::kOk, raw_json);
+  else
+    std::move(callback).Run(GetTemplateJsonStatus::kFailure, "");
+}
+
 }  // namespace desks_storage
diff --git a/components/desks_storage/core/desk_model.h b/components/desks_storage/core/desk_model.h
index d68b047d..2fdef80 100644
--- a/components/desks_storage/core/desk_model.h
+++ b/components/desks_storage/core/desk_model.h
@@ -18,6 +18,10 @@
 class DeskTemplate;
 }
 
+namespace apps {
+class AppRegistryCache;
+}
+
 namespace desks_storage {
 
 class DeskModelObserver;
@@ -58,6 +62,14 @@
     kFailure,
   };
 
+  // status codes for getting template Json representations.
+  enum class GetTemplateJsonStatus {
+    kOk,
+    kNotFound,
+    kInvalidUuid,
+    kFailure,
+  };
+
   DeskModel();
   DeskModel(const DeskModel&) = delete;
   DeskModel& operator=(const DeskModel&) = delete;
@@ -96,6 +108,15 @@
   virtual void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
                                 AddOrUpdateEntryCallback callback) = 0;
 
+  using GetTemplateJsonCallback =
+      base::OnceCallback<void(GetTemplateJsonStatus status,
+                              const std::string& json_representation)>;
+  // Retrieves a template based on its |uuid|, if found returns a std::string
+  // containing the json representation of the template queried.
+  virtual void GetTemplateJson(const std::string& uuid,
+                               apps::AppRegistryCache* app_cache,
+                               GetTemplateJsonCallback callback);
+
   using DeleteEntryCallback =
       base::OnceCallback<void(DeleteEntryStatus status)>;
   // Remove entry with |uuid| from entries. If the entry with |uuid| does not
@@ -141,6 +162,15 @@
 
   // The preconfigured desk templates from policy (as opposed to user-defined)
   std::vector<std::unique_ptr<ash::DeskTemplate>> policy_entries_;
+
+ private:
+  // Handles conversion of DeskTemplate to policy JSON after the queried
+  // DeskTemplate has been retrieved from the implemented class.
+  void HandleTemplateConversionToPolicyJson(
+      GetTemplateJsonCallback callback,
+      apps::AppRegistryCache* app_cache,
+      GetEntryByUuidStatus status,
+      std::unique_ptr<ash::DeskTemplate> entry);
 };
 
 }  // namespace desks_storage
diff --git a/components/desks_storage/core/desk_sync_bridge.cc b/components/desks_storage/core/desk_sync_bridge.cc
index c89f71d..a1794fd 100644
--- a/components/desks_storage/core/desk_sync_bridge.cc
+++ b/components/desks_storage/core/desk_sync_bridge.cc
@@ -11,6 +11,7 @@
 #include "base/callback_helpers.h"
 #include "base/check_op.h"
 #include "base/guid.h"
+#include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
diff --git a/components/desks_storage/core/local_desk_data_manager.cc b/components/desks_storage/core/local_desk_data_manager.cc
index fbf18eb..474afd9 100644
--- a/components/desks_storage/core/local_desk_data_manager.cc
+++ b/components/desks_storage/core/local_desk_data_manager.cc
@@ -17,6 +17,7 @@
 #include "base/values.h"
 #include "components/app_restore/restore_data.h"
 #include "components/desks_storage/core/desk_model.h"
+#include "components/desks_storage/core/desk_template_conversion.h"
 #include "components/sync/protocol/workspace_desk_specifics.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/re2/src/re2/re2.h"
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 17bb1af4..a068765 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -658,7 +658,9 @@
     DLOG(WARNING) << "Surface geometry must be non-empty";
     return;
   }
-
+  if (!widget_) {
+    initial_size_ = gfx::Size(geometry.width(), geometry.height());
+  }
   pending_geometry_ = geometry;
 }
 
@@ -1283,6 +1285,15 @@
   window->AddObserver(this);
   ash::WindowState* window_state = ash::WindowState::Get(window);
   InitializeWindowState(window_state);
+  // TODO(1261321): correct the initial origin once lacros can communicate
+  // it instead of centering.
+  if (show_state == ui::SHOW_STATE_MAXIMIZED) {
+    gfx::Rect screen_size = display::Screen::GetScreen()
+                                ->GetDisplayNearestWindow(window)
+                                .work_area();
+    screen_size.ClampToCenteredSize(initial_size_);
+    window_state->SetRestoreBoundsInParent(screen_size);
+  }
 
   SetShellUseImmersiveForFullscreen(window, immersive_implied_by_fullscreen_);
 
diff --git a/components/exo/shell_surface_base.h b/components/exo/shell_surface_base.h
index e666ea5a..71db71c32 100644
--- a/components/exo/shell_surface_base.h
+++ b/components/exo/shell_surface_base.h
@@ -345,6 +345,7 @@
   int container_;
   gfx::Rect geometry_;
   gfx::Rect pending_geometry_;
+  gfx::Size initial_size_;
   int64_t display_id_ = display::kInvalidDisplayId;
   int64_t pending_display_id_ = display::kInvalidDisplayId;
   absl::optional<gfx::Rect> shadow_bounds_;
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
index 1189e86..02fe45d 100644
--- a/components/exo/shell_surface_unittest.cc
+++ b/components/exo/shell_surface_unittest.cc
@@ -780,7 +780,7 @@
   shell_surface->SetGeometry(geometry);
 
   // Commit without contents should result in a configure callback with empty
-  // suggested size as a mechanims to ask the client size itself.
+  // suggested size as a mechanisms to ask the client size itself.
   surface->Commit();
   EXPECT_TRUE(suggested_size.IsEmpty());
 
@@ -853,7 +853,7 @@
   shell_surface->SetGeometry(geometry);
 
   // Commit without contents should result in a configure callback with empty
-  // suggested size as a mechanims to ask the client size itself.
+  // suggested size as a mechanisms to ask the client size itself.
   surface->Commit();
   EXPECT_TRUE(suggested_size.IsEmpty());
 
@@ -863,7 +863,7 @@
   shell_surface->Minimize();
   shell_surface->AcknowledgeConfigure(0);
   // Commit without contents should result in a configure callback with empty
-  // suggested size as a mechanims to ask the client size itself.
+  // suggested size as a mechanisms to ask the client size itself.
   surface->Commit();
 
   EXPECT_TRUE(shell_surface->GetWidget());
@@ -872,6 +872,55 @@
   EXPECT_EQ(geometry.size(), shell_surface->CalculatePreferredSize());
 }
 
+TEST_F(ShellSurfaceTest, CreateMaximizedWindowWithRestoreBounds) {
+  // Must be before shell_surface so it outlives it, for shell_surface's
+  // destructor calls Configure() referencing these 4 variables.
+  gfx::Size suggested_size;
+  chromeos::WindowStateType has_state_type = chromeos::WindowStateType::kNormal;
+  bool is_resizing = false;
+  bool is_active = false;
+  gfx::Size buffer_size(256, 256);
+  std::unique_ptr<Buffer> buffer(
+      new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
+
+  std::unique_ptr<Surface> surface(new Surface);
+  std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get()));
+
+  shell_surface->set_configure_callback(base::BindRepeating(
+      &Configure, base::Unretained(&suggested_size),
+      base::Unretained(&has_state_type), base::Unretained(&is_resizing),
+      base::Unretained(&is_active)));
+
+  gfx::Rect geometry(0, 0, 1, 1);
+  shell_surface->SetGeometry(geometry);
+  shell_surface->Maximize();
+
+  // Commit without contents should result in a configure callback with empty
+  // suggested size as a mechanisms to ask the client size itself.
+  surface->Attach(buffer.get());
+  surface->Commit();
+  shell_surface->AcknowledgeConfigure(0);
+
+  gfx::Rect geometry_full(0, 0, 100, 100);
+  shell_surface->SetGeometry(geometry_full);
+
+  surface->Commit();
+  shell_surface->AcknowledgeConfigure(1);
+
+  EXPECT_TRUE(shell_surface->GetWidget());
+  EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
+  EXPECT_EQ(geometry_full.size(), shell_surface->CalculatePreferredSize());
+
+  auto* window_state =
+      ash::WindowState::Get(shell_surface->GetWidget()->GetNativeWindow());
+
+  EXPECT_TRUE(window_state->HasRestoreBounds());
+
+  auto bounds = window_state->GetRestoreBoundsInParent();
+  EXPECT_EQ(geometry.width(), bounds.width());
+  EXPECT_EQ(geometry.height(), bounds.height());
+}
+
 TEST_F(ShellSurfaceTest, ToggleFullscreen) {
   gfx::Size buffer_size(256, 256);
   std::unique_ptr<Buffer> buffer(
diff --git a/components/flags_ui/resources/flags.html b/components/flags_ui/resources/flags.html
index 25c867f..2bf3f56 100644
--- a/components/flags_ui/resources/flags.html
+++ b/components/flags_ui/resources/flags.html
@@ -12,6 +12,9 @@
 <title>$i18n{title}</title>
 
 <link rel="stylesheet" href="flags.css">
+<if expr="lacros or chromeos">
+  <link rel="stylesheet" href="chrome://resources/css/os_header.css">
+</if>
 
 <if expr="is_ios">
   <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1, maximum-scale=1">
diff --git a/components/optimization_guide/core/hints_fetcher.cc b/components/optimization_guide/core/hints_fetcher.cc
index 77d628b7..3909ae5 100644
--- a/components/optimization_guide/core/hints_fetcher.cc
+++ b/components/optimization_guide/core/hints_fetcher.cc
@@ -126,7 +126,7 @@
 void HintsFetcher::ClearHostsSuccessfullyFetched(PrefService* pref_service) {
   DictionaryPrefUpdate hosts_fetched_list(
       pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched);
-  hosts_fetched_list->Clear();
+  hosts_fetched_list->DictClear();
 }
 
 void HintsFetcher::SetTimeClockForTesting(const base::Clock* time_clock) {
diff --git a/components/resources/flags_ui_resources.grdp b/components/resources/flags_ui_resources.grdp
index 3a19889..1f46be7 100644
--- a/components/resources/flags_ui_resources.grdp
+++ b/components/resources/flags_ui_resources.grdp
@@ -3,7 +3,4 @@
   <include name="IDR_FLAGS_UI_FLAGS_HTML" file="../flags_ui/resources/flags.html" preprocess="true" type="BINDATA" />
   <include name="IDR_FLAGS_UI_FLAGS_JS" file="../flags_ui/resources/flags.js" preprocess="true" type="BINDATA" />
   <include name="IDR_FLAGS_UI_FLAGS_CSS" file="../flags_ui/resources/flags.css"  preprocess="true" type="BINDATA" />
-  <if expr="lacros or chromeos">
-    <include name="IDR_OS_FLAGS_UI_FLAGS_SVG" file="../flags_ui/resources/os_flags_app_icon.svg"  preprocess="true" type="BINDATA" />
-  </if>
 </grit-part>
diff --git a/components/safe_browsing/core/browser/db/v4_database.cc b/components/safe_browsing/core/browser/db/v4_database.cc
index ee3e070..2184ae2 100644
--- a/components/safe_browsing/core/browser/db/v4_database.cc
+++ b/components/safe_browsing/core/browser/db/v4_database.cc
@@ -15,8 +15,13 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "build/build_config.h"
 #include "components/safe_browsing/core/common/proto/webui.pb.h"
 
+#if defined(OS_APPLE)
+#include "base/mac/backup_util.h"
+#endif
+
 using base::TimeTicks;
 
 namespace safe_browsing {
@@ -87,6 +92,10 @@
   if (!base::CreateDirectory(base_path))
     NOTREACHED();
 
+#if defined(OS_APPLE)
+  base::mac::SetBackupExclusion(base_path);
+#endif
+
   std::unique_ptr<StoreMap> store_map = std::make_unique<StoreMap>();
   for (const auto& it : list_infos) {
     if (!it.fetch_updates()) {
diff --git a/components/signin/core/browser/signin_header_helper_unittest.cc b/components/signin/core/browser/signin_header_helper_unittest.cc
index 9638885..52df4a17 100644
--- a/components/signin/core/browser/signin_header_helper_unittest.cc
+++ b/components/signin/core/browser/signin_header_helper_unittest.cc
@@ -329,7 +329,7 @@
 
 // Mirror is always enabled on Android and iOS, so these tests are only relevant
 // on Desktop.
-#if BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 TEST_F(SigninHeaderHelperTest, TestMirrorRequestGaiaURL) {
   // No request when account consistency is disabled.
@@ -636,7 +636,7 @@
   }
 }
 
-#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) && !BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 // Tests that the Mirror header request is returned normally when the redirect
 // URL is eligible.
diff --git a/components/signin/features.gni b/components/signin/features.gni
index 5e0fe03..e253b1e4 100644
--- a/components/signin/features.gni
+++ b/components/signin/features.gni
@@ -5,8 +5,7 @@
 import("//build/config/chromeos/ui_mode.gni")
 
 # Dice is supported on the platform (but not necessarily enabled).
-enable_dice_support =
-    is_linux || is_chromeos_lacros || is_mac || is_win || is_fuchsia
+enable_dice_support = is_linux || is_mac || is_win || is_fuchsia
 
 # Mirror is enabled and other account consistency mechanisms are not available.
-enable_mirror = is_android || is_chromeos_ash || is_ios
+enable_mirror = is_android || is_chromeos_ash || is_chromeos_lacros || is_ios
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
index c17cc2e..55a8894 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
@@ -52,7 +52,7 @@
       signin_client, std::move(device_accounts_provider),
       account_tracker_service);
 }
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
+#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate(
     AccountTrackerService* account_tracker_service,
     network::NetworkConnectionTracker* network_connection_tracker,
@@ -64,18 +64,6 @@
 }
 #elif BUILDFLAG(ENABLE_DICE_SUPPORT)
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-std::unique_ptr<ProfileOAuth2TokenServiceDelegate> CreateCrOsOAuthDelegate(
-    AccountTrackerService* account_tracker_service,
-    network::NetworkConnectionTracker* network_connection_tracker,
-    account_manager::AccountManagerFacade* account_manager_facade,
-    bool is_regular_profile) {
-  return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>(
-      account_tracker_service, network_connection_tracker,
-      account_manager_facade, is_regular_profile);
-}
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-
 std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate>
 CreateMutableProfileOAuthDelegate(
     AccountTrackerService* account_tracker_service,
@@ -133,25 +121,10 @@
   return CreateIOSOAuthDelegate(signin_client,
                                 std::move(device_accounts_provider),
                                 account_tracker_service);
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
+#elif BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   return CreateCrOsOAuthDelegate(account_tracker_service,
                                  network_connection_tracker,
                                  account_manager_facade, is_regular_profile);
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
-  // For the time being, Mirror is enabled only in the first / "Main" Profile in
-  // Lacros.
-  if (account_consistency == signin::AccountConsistencyMethod::kMirror) {
-    return CreateCrOsOAuthDelegate(account_tracker_service,
-                                   network_connection_tracker,
-                                   account_manager_facade, is_regular_profile);
-  } else {
-    // TODO(crbug.com/1198490): Remove this when we don't need DICE and
-    // `MutableProfileOAuth2TokenServiceDelegate` on Lacros anymore.
-    return CreateMutableProfileOAuthDelegate(
-        account_tracker_service, account_consistency,
-        delete_signin_cookies_on_exit, token_web_data, signin_client,
-        network_connection_tracker);
-  }
 #elif BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Fall back to |MutableProfileOAuth2TokenServiceDelegate| on all platforms
   // other than Android, iOS, and Chrome OS (Ash and Lacros).
diff --git a/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/AdsBlockedDialog.java b/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/AdsBlockedDialog.java
index a6e54ea..789c741 100644
--- a/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/AdsBlockedDialog.java
+++ b/components/subresource_filter/android/java/src/org/chromium/components/subresource_filter/AdsBlockedDialog.java
@@ -11,7 +11,6 @@
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.style.ClickableSpan;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -26,6 +25,7 @@
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.text.NoUnderlineClickableSpan;
 
 /**
  * Java part of AdsBlockedDialog pair providing communication between native ads blocked
@@ -82,12 +82,8 @@
     @CalledByNative
     void show(boolean shouldPostDialog) {
         Resources resources = mContext.getResources();
-        mClickableSpan = new ClickableSpan() {
-            @Override
-            public void onClick(View view) {
-                AdsBlockedDialogJni.get().onLearnMoreClicked(mNativeDialog);
-            }
-        };
+        mClickableSpan = new NoUnderlineClickableSpan(
+                resources, (view) -> AdsBlockedDialogJni.get().onLearnMoreClicked(mNativeDialog));
         mDialogModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                                .with(ModalDialogProperties.CONTROLLER, this)
                                .with(ModalDialogProperties.TITLE, resources,
diff --git a/components/ukm/app_source_url_recorder_test.cc b/components/ukm/app_source_url_recorder_test.cc
index 8e6f45c9..7823f38 100644
--- a/components/ukm/app_source_url_recorder_test.cc
+++ b/components/ukm/app_source_url_recorder_test.cc
@@ -51,7 +51,7 @@
 };
 
 TEST_F(AppSourceUrlRecorderTest, CheckChromeApp) {
-  const std::string app_id = "hhaomjibdihmijegdhdafkllkbggdgoj";
+  const std::string app_id = "unique_app_id";
   SourceId id = GetSourceIdForChromeApp(app_id);
   GURL expected_url("app://" + app_id);
 
diff --git a/components/version_ui/resources/about_version.html b/components/version_ui/resources/about_version.html
index 9392131..42cabec4 100644
--- a/components/version_ui/resources/about_version.html
+++ b/components/version_ui/resources/about_version.html
@@ -21,11 +21,27 @@
 <if expr="is_android or is_ios">
     <link rel="stylesheet" href="about_version_mobile.css">
 </if>
+<if expr="lacros or chromeos">
+    <link rel="stylesheet" href="chrome://resources/css/os_header.css">
+</if>
 
     <script type ="module" src="about_version.js"></script>
   </head>
 
   <body>
+<if expr="lacros or chromeos">
+    <div class="os-link-container-container" id="os-link-container" hidden>
+      <div class="os-link-container">
+        <span class="os-link-icon"></span>
+        <span aria-hidden="true" id="os-link-desc">$i18n{os-version-text1}</span>
+        <a href="#" id="os-link-href" aria-describedby="os-link-desc">
+          $i18n{os-version-link}
+        </a>
+        <span aria-hidden="true">$i18n{os-version-text2}</span>
+      </div>
+    </div>
+  </if>
+
     <div id="outer">
       <div id="logo">
 <if expr="not is_android and not is_ios">
diff --git a/components/version_ui/resources/about_version.js b/components/version_ui/resources/about_version.js
index 5e53264..f986650 100644
--- a/components/version_ui/resources/about_version.js
+++ b/components/version_ui/resources/about_version.js
@@ -82,6 +82,29 @@
 }
 // </if>
 
+// <if expr="chromeos or lacros">
+/**
+ * Callback from the backend to inform if Lacros is primary or not.
+ * @param {string} isPrimary True if it is primary.
+ */
+function returnLacrosPrimary(isPrimary) {
+  $('os-link-container').hidden = !isPrimary;
+
+  const crosUrlRedirectButton = $('os-link-href');
+  if (crosUrlRedirectButton) {
+    crosUrlRedirectButton.onclick = crosUrlVersionRedirect;
+  }
+}
+
+/**
+ * Called when the user clicks on the os-link-href button.
+ */
+function crosUrlVersionRedirect() {
+  chrome.send('crosUrlVersionRedirect');
+}
+
+// </if>
+
 function copyToClipboard() {
   navigator.clipboard.writeText($('copy-content').innerText);
 }
@@ -95,6 +118,9 @@
   addWebUIListener('return-os-firmware-version', returnOsFirmwareVersion);
   addWebUIListener('return-arc-version', returnARCVersion);
   // </if>
+  // <if expr="chromeos or lacros">
+  addWebUIListener('return-lacros-primary', returnLacrosPrimary);
+  // </if>
 
   chrome.send('requestVersionInfo');
   const includeVariationsCmd = location.search.includes("show-variations-cmd");
diff --git a/components/version_ui/version_ui_constants.cc b/components/version_ui/version_ui_constants.cc
index e354878..ca4d1452 100644
--- a/components/version_ui/version_ui_constants.cc
+++ b/components/version_ui/version_ui_constants.cc
@@ -71,6 +71,11 @@
 const char kProfilePath[] = "profile_path";
 const char kProfilePathName[] = "profile_path_name";
 #endif
+#if defined(OS_CHROMEOS)
+const char kOsVersionHeaderText1[] = "os-version-text1";
+const char kOsVersionHeaderText2[] = "os-version-text2";
+const char kOsVersionHeaderLink[] = "os-version-link";
+#endif
 const char kRevision[] = "revision";
 const char kSanitizer[] = "sanitizer";
 const char kTitle[] = "title";
diff --git a/components/version_ui/version_ui_constants.h b/components/version_ui/version_ui_constants.h
index 332d0db..b981079 100644
--- a/components/version_ui/version_ui_constants.h
+++ b/components/version_ui/version_ui_constants.h
@@ -75,6 +75,11 @@
 extern const char kProfilePath[];
 extern const char kProfilePathName[];
 #endif
+#if defined(OS_CHROMEOS)
+extern const char kOsVersionHeaderText1[];
+extern const char kOsVersionHeaderText2[];
+extern const char kOsVersionHeaderLink[];
+#endif
 extern const char kRevision[];
 extern const char kSanitizer[];
 extern const char kTitle[];
diff --git a/components/version_ui_strings.grdp b/components/version_ui_strings.grdp
index 6fb0de0..e6d9dfa 100644
--- a/components/version_ui_strings.grdp
+++ b/components/version_ui_strings.grdp
@@ -74,4 +74,26 @@
       (cohort: <ph name="UPDATE_COHORT_NAME">$1<ex>Stable</ex></ph>)
     </message>
   </if>
+  <if expr="chromeos">
+    <message name="IDS_VERSION_UI_OS_TEXT1_LABEL" desc="Begin label text on the os://version page from ChromeOS which directs a user to a dialog showing Chrome browser specific version - enclosing url link.">
+      Looking for browser version? Visit
+    </message>
+    <message name="IDS_VERSION_UI_OS_TEXT2_LABEL" desc="End label text on the os://version page from ChromeOS which directs a user to a dialog showing Chrome browser specific version - enclosing url link.">
+      .
+    </message>
+    <message name="IDS_VERSION_UI_OS_LINK" translateable="false" desc="Label for a button on the os://version page from ChromeOS that will call a new window which allows to change Chrome browser version.">
+      chrome://version
+    </message>
+  </if>
+  <if expr="lacros">
+    <message name="IDS_VERSION_UI_OS_TEXT1_LABEL" desc="Begin label text on the chrome://version page from ChromeOS which directs a user to a dialog showing OS specific version - enclosing url link.">
+      Looking for system version? Visit
+    </message>
+    <message name="IDS_VERSION_UI_OS_TEXT2_LABEL" desc="End label text on the chrome://version page from ChromeOS which directs a user to a dialog showing OS specific version - enclosing url link.">
+      .
+    </message>
+    <message name="IDS_VERSION_UI_OS_LINK" translateable="false" desc="Label for button on the chrome://version page from ChromeOS that will call a new window which allows to change system version.">
+      os://version
+    </message>
+  </if>
 </grit-part>
diff --git a/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_LINK.png.sha1 b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_LINK.png.sha1
new file mode 100644
index 0000000..183d5e0e
--- /dev/null
+++ b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_LINK.png.sha1
@@ -0,0 +1 @@
+5883189dc66d0f4de865e7994894ec49c438751e
\ No newline at end of file
diff --git a/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT1_LABEL.png.sha1 b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT1_LABEL.png.sha1
new file mode 100644
index 0000000..183d5e0e
--- /dev/null
+++ b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT1_LABEL.png.sha1
@@ -0,0 +1 @@
+5883189dc66d0f4de865e7994894ec49c438751e
\ No newline at end of file
diff --git a/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT2_LABEL.png.sha1 b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT2_LABEL.png.sha1
new file mode 100644
index 0000000..183d5e0e
--- /dev/null
+++ b/components/version_ui_strings_grdp/IDS_VERSION_UI_OS_TEXT2_LABEL.png.sha1
@@ -0,0 +1 @@
+5883189dc66d0f4de865e7994894ec49c438751e
\ No newline at end of file
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 6e94079..d5278b7 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -61,7 +61,6 @@
 
 using ::testing::_;
 using ::testing::ElementsAre;
-using ::testing::Key;
 
 constexpr FrameSinkId kArbitraryRootFrameSinkId(1, 1);
 constexpr FrameSinkId kArbitraryFrameSinkId1(2, 2);
@@ -139,9 +138,16 @@
 
   AggregatedFrame AggregateFrame(const SurfaceId& surface_id,
                                  gfx::Rect target_damage = gfx::Rect()) {
-    return aggregator_.Aggregate(
+    AggregatedFrame aggregated_frame = aggregator_.Aggregate(
         surface_id, GetNextDisplayTimeAndIncrement(),
         /*display_transform=*/gfx::OVERLAY_TRANSFORM_NONE, target_damage);
+
+    // Ensure no duplicate pass ids output.
+    std::set<AggregatedRenderPassId> used_passes;
+    for (const auto& pass : aggregated_frame.render_pass_list)
+      EXPECT_TRUE(used_passes.insert(pass->id).second);
+
+    return aggregated_frame;
   }
 
   struct Quad {
@@ -528,14 +534,16 @@
     TestPassesMatchExpectations(expected_passes,
                                 &aggregated_frame.render_pass_list);
     VerifyQuadCoverSQS(&aggregated_frame);
+    VerifyExpectedSurfaceIds(expected_surface_ids);
+  }
 
-    // Ensure no duplicate pass ids output.
-    std::set<AggregatedRenderPassId> used_passes;
-    for (const auto& pass : aggregated_frame.render_pass_list)
-      EXPECT_TRUE(used_passes.insert(pass->id).second);
-
+  void VerifyExpectedSurfaceIds(
+      const std::vector<SurfaceId>& expected_surface_ids) {
     EXPECT_EQ(expected_surface_ids.size(),
               aggregator_.previous_contained_surfaces().size());
+    EXPECT_EQ(expected_surface_ids.size(),
+              aggregator_.previous_contained_frame_sinks().size());
+
     for (const SurfaceId& surface_id : expected_surface_ids) {
       EXPECT_THAT(aggregator_.previous_contained_surfaces(),
                   testing::Contains(testing::Key(surface_id)));
@@ -582,26 +590,6 @@
                           std::move(referenced_surfaces), device_scale_factor);
   }
 
-  CompositorFrame MakeCompositorFrameFromSurfaceRanges(
-      const std::vector<SurfaceRange>& ranges) {
-    std::vector<Quad> quads;
-    for (const SurfaceRange& range : ranges) {
-      quads.push_back(Quad::SurfaceQuad(
-          range, SK_ColorWHITE, gfx::Rect(5, 5), 1.f, gfx::Transform(),
-          /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo(),
-          /*is_fast_rounded_corner=*/false));
-    }
-    std::vector<Pass> passes = {Pass(quads, kSurfaceSize)};
-    CompositorRenderPassList pass_list;
-    std::vector<SurfaceRange> referenced_surfaces;
-    AddPasses(&pass_list, passes, &referenced_surfaces);
-    return CompositorFrameBuilder()
-        .SetRenderPassList(std::move(pass_list))
-        .SetDeviceScaleFactor(1.f)
-        .SetReferencedSurfaces(ranges)
-        .Build();
-  }
-
   void QueuePassAsFrame(std::unique_ptr<CompositorRenderPass> pass,
                         const LocalSurfaceId& local_surface_id,
                         float device_scale_factor,
@@ -641,30 +629,36 @@
 // Tests that a very simple frame containing only two solid color quads makes it
 // through the aggregator correctly.
 TEST_F(SurfaceAggregatorValidSurfaceTest, SimpleFrame) {
-  std::vector<Quad> quads = {
-      Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
-  std::vector<Pass> passes = {Pass(quads, kSurfaceSize)};
+  CompositorFrame frame =
+      CompositorFrameBuilder()
+          .AddRenderPass(RenderPassBuilder(kSurfaceSize)
+                             .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorRED)
+                             .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLUE))
+          .Build();
+
+  root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                    std::move(frame));
 
   // Add a callback for when the surface is damaged.
   MockAggregatedDamageCallback aggregated_damage_callback;
   root_sink_->SetAggregatedDamageCallbackForTesting(
       aggregated_damage_callback.GetCallback());
 
-  constexpr float device_scale_factor = 1.0f;
-  SubmitCompositorFrame(root_sink_.get(), passes,
-                        root_surface_id_.local_surface_id(),
-                        device_scale_factor);
-
   // Check that the AggregatedDamageCallback is called with the right arguments.
   EXPECT_CALL(
       aggregated_damage_callback,
       OnAggregatedDamage(root_surface_id_.local_surface_id(), kSurfaceSize,
                          gfx::Rect(kSurfaceSize), next_display_time()));
 
-  AggregateAndVerify(passes, {root_surface_id_});
+  auto aggregated_frame = AggregateFrame(root_surface_id_);
+  EXPECT_EQ(aggregated_frame.render_pass_list.size(), 1u);
 
-  testing::Mock::VerifyAndClearExpectations(&aggregated_damage_callback);
+  auto& render_pass = aggregated_frame.render_pass_list[0];
+  EXPECT_THAT(render_pass->quad_list,
+              ElementsAre(IsSolidColorQuad(SK_ColorRED),
+                          IsSolidColorQuad(SK_ColorBLUE)));
+
+  VerifyExpectedSurfaceIds({root_surface_id_});
 }
 
 // Test that when surface is translucent and we need the render surface to apply
@@ -739,37 +733,44 @@
       nullptr, &manager_, kArbitraryFrameSinkId1, /*is_root=*/true);
   TestSurfaceIdAllocator embedded_surface_id(embedded_support->frame_sink_id());
 
-  std::vector<Quad> embedded_quads = {
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
-  std::vector<Pass> embedded_passes = {Pass(embedded_quads, kSurfaceSize)};
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLUE))
+            .Build();
+    embedded_support->SubmitCompositorFrame(
+        embedded_surface_id.local_surface_id(), std::move(frame));
+  }
 
-  constexpr float device_scale_factor = 1.0f;
-
-  SubmitCompositorFrame(embedded_support.get(), embedded_passes,
-                        embedded_surface_id.local_surface_id(),
-                        device_scale_factor);
   gfx::Transform rotate;
   rotate.Rotate(30);
-  std::vector<Quad> quads = {Quad::SurfaceQuad(
-      SurfaceRange(absl::nullopt, embedded_surface_id), SK_ColorWHITE,
-      gfx::Rect(5, 5), 1.f, rotate,
-      /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo(),
-      /*is_fast_rounded_corner=*/false)};
-  std::vector<Pass> passes = {Pass(quads, kSurfaceSize)};
 
-  SubmitCompositorFrame(root_sink_.get(), passes,
-                        root_surface_id_.local_surface_id(),
-                        device_scale_factor);
+  CompositorFrame frame =
+      CompositorFrameBuilder()
+          .AddRenderPass(RenderPassBuilder(kSurfaceSize)
+                             .AddSurfaceQuad(gfx::Rect(5, 5),
+                                             SurfaceRange(absl::nullopt,
+                                                          embedded_surface_id))
+                             .SetQuadToTargetTransform(rotate))
+          .Build();
+  root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                    std::move(frame));
 
   auto aggregated_frame = AggregateFrame(root_surface_id_);
+  ASSERT_EQ(2u, aggregated_frame.render_pass_list.size());
 
-  auto& render_pass_list = aggregated_frame.render_pass_list;
-  EXPECT_EQ(2u, render_pass_list.size());
+  auto& embedded_pass = aggregated_frame.render_pass_list[0];
+  EXPECT_THAT(embedded_pass->quad_list,
+              ElementsAre(IsSolidColorQuad(SK_ColorGREEN),
+                          IsSolidColorQuad(SK_ColorBLUE)));
 
-  auto& shared_quad_state_list2 =
-      render_pass_list.back()->shared_quad_state_list;
-  EXPECT_EQ(rotate, shared_quad_state_list2.front()->quad_to_target_transform);
+  auto& root_pass = aggregated_frame.render_pass_list[1];
+  EXPECT_THAT(
+      root_pass->quad_list,
+      ElementsAre(AllOf(IsAggregatedRenderPassQuad(), HasTransform(rotate))));
 }
 
 TEST_F(SurfaceAggregatorValidSurfaceTest, MultiPassSimpleFrame) {
@@ -954,34 +955,42 @@
       nullptr, &manager_, kArbitraryFrameSinkId1, /*is_root=*/true);
   TestSurfaceIdAllocator embedded_surface_id(embedded_support->frame_sink_id());
 
-  std::vector<Quad> embedded_quads = {
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5))};
-  std::vector<Pass> embedded_passes = {Pass(embedded_quads, kSurfaceSize)};
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN))
+            .Build();
+    embedded_support->SubmitCompositorFrame(
+        embedded_surface_id.local_surface_id(), std::move(frame));
+  }
 
-  constexpr float device_scale_factor = 1.0f;
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorWHITE)
+                    .AddSurfaceQuad(
+                        gfx::Rect(5, 5),
+                        SurfaceRange(absl::nullopt, embedded_surface_id))
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLACK))
+            .Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                      std::move(frame));
+  }
 
-  SubmitCompositorFrame(embedded_support.get(), embedded_passes,
-                        embedded_surface_id.local_surface_id(),
-                        device_scale_factor);
+  auto aggregated_frame = AggregateFrame(root_surface_id_);
+  ASSERT_EQ(aggregated_frame.render_pass_list.size(), 1u);
 
-  std::vector<Quad> root_quads = {
-      Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
-      Quad::SurfaceQuad(SurfaceRange(absl::nullopt, embedded_surface_id),
-                        SK_ColorWHITE, gfx::Rect(5, 5),
-                        /*stretch_content_to_fill_bounds=*/false),
-      Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
-  std::vector<Pass> root_passes = {Pass(root_quads, kSurfaceSize)};
+  auto& render_pass = aggregated_frame.render_pass_list[0];
+  EXPECT_THAT(render_pass->quad_list,
+              ElementsAre(IsSolidColorQuad(SK_ColorWHITE),
+                          IsSolidColorQuad(SK_ColorGREEN),
+                          IsSolidColorQuad(SK_ColorBLACK)));
 
-  SubmitCompositorFrame(root_sink_.get(), root_passes,
-                        root_surface_id_.local_surface_id(),
-                        device_scale_factor);
-
-  std::vector<Quad> expected_quads = {
-      Quad::SolidColorQuad(SK_ColorWHITE, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLACK, gfx::Rect(5, 5))};
-  std::vector<Pass> expected_passes = {Pass(expected_quads, kSurfaceSize)};
-  AggregateAndVerify(expected_passes, {root_surface_id_, embedded_surface_id});
+  VerifyExpectedSurfaceIds({root_surface_id_, embedded_surface_id});
 }
 
 class TestVizClient {
@@ -1246,12 +1255,11 @@
 
   constexpr gfx::Rect root_quad_rect(10, 10);
   {
-    auto pass =
-        RenderPassBuilder(CompositorRenderPassId{1}, gfx::Rect(kSurfaceSize))
-            .AddSolidColorQuad(root_quad_rect, SK_ColorRED)
-            .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
-                            SurfaceRange(child_surface_id))
-            .Build();
+    auto pass = RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(root_quad_rect, SK_ColorRED)
+                    .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                                    SurfaceRange(child_surface_id))
+                    .Build();
 
     CompositorFrame frame = CompositorFrameBuilder()
                                 .AddRenderPass(std::move(pass))
@@ -1318,7 +1326,6 @@
 // This test verifies that the appropriate transform will be applied to a
 // surface embedded by a parent SurfaceDrawQuad marked as
 // stretch_content_to_fill_bounds.
-
 TEST_F(SurfaceAggregatorValidSurfaceTest, StretchContentToFillBounds) {
   auto primary_child_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryFrameSinkId1, /*is_root=*/false);
@@ -1329,7 +1336,7 @@
     CompositorFrame frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1}, gfx::Rect(20, 20))
+                RenderPassBuilder(gfx::Rect(20, 20))
                     .AddSolidColorQuad(gfx::Rect(20, 20), SK_ColorRED))
             .Build();
 
@@ -1346,8 +1353,7 @@
     CompositorFrame frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(kSurfaceSize)
                     .AddSurfaceQuad(surface_quad_rect,
                                     SurfaceRange(primary_child_surface_id),
                                     {.stretch_content_to_fill_bounds = true}))
@@ -1456,37 +1462,39 @@
   TestSurfaceIdAllocator primary_child_surface_id(
       primary_child_support->frame_sink_id());
 
+  constexpr gfx::Rect child_surface_rect(20, 20);
   {
-    auto pass = CompositorRenderPass::Create();
-    pass->SetNew(CompositorRenderPassId{1}, gfx::Rect(0, 0, 20, 20),
-                 gfx::Rect(), gfx::Transform());
-    auto* sqs = pass->CreateAndAppendSharedQuadState();
-    auto* solid_color_quad =
-        pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-
-    solid_color_quad->SetNew(sqs, gfx::Rect(0, 0, 20, 20),
-                             gfx::Rect(0, 0, 20, 20), SK_ColorRED, false);
-
     CompositorFrame frame =
-        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(child_surface_rect)
+                    .AddSolidColorQuad(child_surface_rect, SK_ColorRED))
+            .SetDeviceScaleFactor(1.0f)
+            .Build();
 
     primary_child_support->SubmitCompositorFrame(
         primary_child_surface_id.local_surface_id(), std::move(frame));
   }
 
   constexpr gfx::Rect surface_quad_rect(10, 5);
-  std::vector<Quad> root_quads = {Quad::SurfaceQuad(
-      SurfaceRange(primary_child_surface_id), SK_ColorWHITE, surface_quad_rect,
-      /*stretch_content_to_fill_bounds=*/true)};
-  std::vector<Pass> root_passes = {Pass(root_quads, kSurfaceSize)};
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSurfaceQuad(surface_quad_rect,
+                                    SurfaceRange(primary_child_surface_id),
+                                    {.stretch_content_to_fill_bounds = true}))
+            .SetDeviceScaleFactor(0.5f)
+            .Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                      std::move(frame));
+  }
 
   MockAggregatedDamageCallback aggregated_damage_callback;
   root_sink_->SetAggregatedDamageCallbackForTesting(
       aggregated_damage_callback.GetCallback());
 
-  SubmitCompositorFrame(root_sink_.get(), root_passes,
-                        root_surface_id_.local_surface_id(), 0.5f);
-
   EXPECT_CALL(
       aggregated_damage_callback,
       OnAggregatedDamage(root_surface_id_.local_surface_id(), kSurfaceSize,
@@ -1496,19 +1504,15 @@
 
   EXPECT_EQ(1u, frame.render_pass_list.size());
   auto* render_pass = frame.render_pass_list.back().get();
-  EXPECT_EQ(1u, render_pass->quad_list.size());
 
-  auto* output_quad = render_pass->quad_list.back();
+  // SurfaceAggregator should stretch the 20x20 SolidColorDrawQuad to fit the
+  // bounds of the parent's 10x5 SurfaceDrawQuad.
+  gfx::Transform expected_transform;
+  expected_transform.Scale(0.5, 0.25);
 
-  EXPECT_EQ(DrawQuad::Material::kSolidColor, output_quad->material);
-  gfx::RectF output_rect(50.f, 50.f);
-
-  // SurfaceAggregator should stretch the SolidColorDrawQuad to fit the bounds
-  // of the parent's SurfaceDrawQuad.
-  output_quad->shared_quad_state->quad_to_target_transform.TransformRect(
-      &output_rect);
-
-  EXPECT_EQ(gfx::RectF(25.f, 12.5f), output_rect);
+  EXPECT_THAT(render_pass->quad_list,
+              ElementsAre(AllOf(IsSolidColorQuad(), HasRect(child_surface_rect),
+                                HasTransform(expected_transform))));
 }
 
 // Verify that a reflected SurfaceDrawQuad with scaling won't have the surfaces
@@ -1578,19 +1582,11 @@
   // by the second display through surface embedding.
   const gfx::Rect display_rect(0, 0, 100, 100);
   {
-    auto pass = CompositorRenderPass::Create();
-    pass->SetNew(CompositorRenderPassId{1}, display_rect, display_rect,
-                 gfx::Transform());
-    auto* sqs = pass->CreateAndAppendSharedQuadState();
-    sqs->opacity = 1.f;
-
-    auto* solid_color_quad =
-        pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-    solid_color_quad->SetNew(sqs, display_rect, display_rect, SK_ColorRED,
-                             false);
-
     CompositorFrame frame =
-        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+        CompositorFrameBuilder()
+            .AddRenderPass(RenderPassBuilder(display_rect)
+                               .AddSolidColorQuad(display_rect, SK_ColorRED))
+            .Build();
     root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
                                       std::move(frame));
   }
@@ -1608,24 +1604,17 @@
   translate_transform.Translate(10, 0);
 
   {
-    auto pass = CompositorRenderPass::Create();
-    pass->SetNew(CompositorRenderPassId{1}, mirror_display_rect,
-                 mirror_display_rect, gfx::Transform());
-    auto* sqs = pass->CreateAndAppendSharedQuadState();
-    sqs->quad_to_target_transform = translate_transform;
-    sqs->opacity = 1.f;
-
-    auto* surface_quad = pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-    surface_quad->SetAll(sqs, display_rect, display_rect,
-                         /*needs_blending=*/false,
-                         SurfaceRange(absl::nullopt, root_surface_id_),
-                         SK_ColorBLACK,
-                         /*stretch_content_to_fill_bounds=*/true,
-                         /*is_reflection=*/true,
-                         /*allow_merge=*/true);
-
     CompositorFrame frame =
-        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(mirror_display_rect)
+                    .AddSurfaceQuad(
+                        display_rect,
+                        SurfaceRange(absl::nullopt, root_surface_id_),
+                        {.stretch_content_to_fill_bounds = true,
+                         .is_reflection = true})
+                    .SetQuadToTargetTransform(translate_transform))
+            .Build();
     mirror_display_sink->SubmitCompositorFrame(
         mirror_display_surface_id.local_surface_id(), std::move(frame));
   }
@@ -1637,15 +1626,12 @@
   EXPECT_EQ(1u, frame.render_pass_list.size());
 
   auto* root_render_pass = frame.render_pass_list.back().get();
-  EXPECT_EQ(1u, root_render_pass->quad_list.size());
-
-  auto* output_quad = root_render_pass->quad_list.back();
-  EXPECT_EQ(DrawQuad::Material::kSolidColor, output_quad->material);
 
   // The quad from the embedded surface merged into the root RenderPass should
   // have the same translate transform that was applied to the SurfaceDrawQuad.
-  EXPECT_EQ(output_quad->shared_quad_state->quad_to_target_transform,
-            translate_transform);
+  EXPECT_THAT(root_render_pass->quad_list,
+              ElementsAre(AllOf(IsSolidColorQuad(),
+                                HasTransform(translate_transform))));
 }
 
 // This test verifies that in the presence of both primary Surface and fallback
@@ -1791,9 +1777,7 @@
   DCHECK_EQ(copy_request_ptr,
             aggregated_frame.render_pass_list[0]->copy_requests[0].get());
 
-  EXPECT_THAT(aggregator_.previous_contained_surfaces(),
-              testing::UnorderedElementsAre(Key(root_surface_id_),
-                                            Key(embedded_surface_id)));
+  VerifyExpectedSurfaceIds({root_surface_id_, embedded_surface_id});
 }
 
 TEST_F(SurfaceAggregatorValidSurfaceTest,
@@ -1898,9 +1882,7 @@
   EXPECT_THAT(render_pass_list[1]->copy_requests,
               ElementsAre(testing::Pointer(root_copy_request_ptr.get())));
 
-  EXPECT_THAT(aggregator_.previous_contained_surfaces(),
-              testing::UnorderedElementsAre(Key(root_surface_id_),
-                                            Key(embedded_surface_id)));
+  VerifyExpectedSurfaceIds({root_surface_id_, embedded_surface_id});
 
   // Ensure copy requests have been removed from root surface.
   const CompositorFrame& original_frame =
@@ -2034,10 +2016,8 @@
   DCHECK_EQ(copy_request_ptr,
             aggregated_frame.render_pass_list[0]->copy_requests[0].get());
 
-  EXPECT_THAT(aggregator_.previous_contained_surfaces(),
-              testing::UnorderedElementsAre(Key(root_surface_id_),
-                                            Key(parent_surface_id),
-                                            Key(embedded_surface_id)));
+  VerifyExpectedSurfaceIds(
+      {root_surface_id_, parent_surface_id, embedded_surface_id});
 }
 
 // This tests referencing a surface that has multiple render passes.
@@ -2397,30 +2377,6 @@
                 ->render_pass_id);
 }
 
-void AddSolidColorQuadWithBlendMode(
-    const gfx::Size& size,
-    CompositorRenderPass* pass,
-    const SkBlendMode blend_mode,
-    const gfx::MaskFilterInfo& mask_filter_info) {
-  const gfx::Transform layer_to_target_transform;
-  const gfx::Rect layer_rect(size);
-  const gfx::Rect visible_layer_rect(size);
-
-  SkColor color = SK_ColorGREEN;
-  bool are_contents_opaque = SkColorGetA(color) == 0xFF;
-  float opacity = 1.f;
-
-  bool force_anti_aliasing_off = false;
-  auto* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(layer_to_target_transform, layer_rect, visible_layer_rect,
-              mask_filter_info, absl::nullopt, are_contents_opaque, opacity,
-              blend_mode, 0);
-
-  auto* color_quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
-  color_quad->SetNew(pass->shared_quad_state_list.back(), visible_layer_rect,
-                     visible_layer_rect, color, force_anti_aliasing_off);
-}
-
 // This tests that we update shared quad state pointers correctly within
 // aggregated passes.  The shared quad state list on the aggregated pass will
 // include the shared quad states from each pass in one list so the quads will
@@ -2464,19 +2420,16 @@
       nullptr, &manager_, kArbitraryFrameSinkId2, /*is_root=*/false);
   auto child_two_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryFrameSinkId3, /*is_root=*/false);
-  CompositorRenderPassId pass_id{1};
   TestSurfaceIdAllocator grandchild_surface_id(
       grandchild_support->frame_sink_id());
 
-  auto grandchild_pass = CompositorRenderPass::Create();
   constexpr float device_scale_factor = 1.0f;
-  gfx::Rect output_rect(kSurfaceSize);
-  gfx::Rect damage_rect(kSurfaceSize);
-  gfx::Transform transform_to_root_target;
-  grandchild_pass->SetNew(pass_id, output_rect, damage_rect,
-                          transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, grandchild_pass.get(),
-                                 blend_modes[2], gfx::MaskFilterInfo());
+
+  auto grandchild_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[2])
+          .Build();
   QueuePassAsFrame(std::move(grandchild_pass),
                    grandchild_surface_id.local_surface_id(),
                    device_scale_factor, grandchild_support.get());
@@ -2484,20 +2437,15 @@
   TestSurfaceIdAllocator child_one_surface_id(
       child_one_support->frame_sink_id());
 
-  auto child_one_pass = CompositorRenderPass::Create();
-  child_one_pass->SetNew(pass_id, output_rect, damage_rect,
-                         transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_one_pass.get(),
-                                 blend_modes[1], gfx::MaskFilterInfo());
-  auto* grandchild_surface_quad =
-      child_one_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  grandchild_surface_quad->SetNew(
-      child_one_pass->shared_quad_state_list.back(), gfx::Rect(kSurfaceSize),
-      gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, grandchild_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_one_pass.get(),
-                                 blend_modes[3], gfx::MaskFilterInfo());
+  auto child_one_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[1])
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, grandchild_surface_id))
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[3])
+          .Build();
   QueuePassAsFrame(std::move(child_one_pass),
                    child_one_surface_id.local_surface_id(), device_scale_factor,
                    child_one_support.get());
@@ -2505,40 +2453,28 @@
   TestSurfaceIdAllocator child_two_surface_id(
       child_two_support->frame_sink_id());
 
-  auto child_two_pass = CompositorRenderPass::Create();
-  child_two_pass->SetNew(pass_id, output_rect, damage_rect,
-                         transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_two_pass.get(),
-                                 blend_modes[5], gfx::MaskFilterInfo());
+  auto child_two_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[5])
+          .Build();
   QueuePassAsFrame(std::move(child_two_pass),
                    child_two_surface_id.local_surface_id(), device_scale_factor,
                    child_two_support.get());
 
-  auto root_pass = CompositorRenderPass::Create();
-  root_pass->SetNew(pass_id, output_rect, damage_rect,
-                    transform_to_root_target);
-
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, root_pass.get(), blend_modes[0],
-                                 gfx::MaskFilterInfo());
-  auto* child_one_surface_quad =
-      root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_one_surface_quad->SetNew(
-      root_pass->shared_quad_state_list.back(), gfx::Rect(kSurfaceSize),
-      gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_one_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, root_pass.get(), blend_modes[4],
-                                 gfx::MaskFilterInfo());
-  auto* child_two_surface_quad =
-      root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_two_surface_quad->SetNew(
-      root_pass->shared_quad_state_list.back(), gfx::Rect(kSurfaceSize),
-      gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_two_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, root_pass.get(), blend_modes[6],
-                                 gfx::MaskFilterInfo());
-
+  auto root_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[0])
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_one_surface_id))
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[4])
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_two_surface_id))
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetBlendMode(blend_modes[6])
+          .Build();
   QueuePassAsFrame(std::move(root_pass), root_surface_id_.local_surface_id(),
                    device_scale_factor, root_sink_.get());
 
@@ -2599,22 +2535,17 @@
       nullptr, &manager_, kArbitraryMiddleFrameSinkId, /*is_root=*/false);
   auto child_three_support = std::make_unique<CompositorFrameSinkSupport>(
       nullptr, &manager_, kArbitraryFrameSinkId3, /*is_root=*/false);
-  CompositorRenderPassId pass_id{1};
 
-  gfx::Rect output_rect(kSurfaceSize);
-  gfx::Rect damage_rect(kSurfaceSize);
   constexpr float device_scale_factor = 1.0f;
-  gfx::Transform transform_to_root_target;
 
   // Setup childe three surface.
   TestSurfaceIdAllocator child_three_surface_id(
       child_three_support->frame_sink_id());
 
-  auto child_three_pass = CompositorRenderPass::Create();
-  child_three_pass->SetNew(pass_id, output_rect, damage_rect,
-                           transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_three_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
+  auto child_three_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .Build();
   QueuePassAsFrame(std::move(child_three_pass),
                    child_three_surface_id.local_surface_id(),
                    device_scale_factor, child_three_support.get());
@@ -2623,23 +2554,12 @@
   TestSurfaceIdAllocator child_one_surface_id(
       child_one_support->frame_sink_id());
 
-  auto child_one_pass = CompositorRenderPass::Create();
-  child_one_pass->SetNew(pass_id, output_rect, damage_rect,
-                         transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_one_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
-
-  // Add child three surface quad
-  auto* child_three_surface_sqs =
-      child_one_pass->CreateAndAppendSharedQuadState();
-  child_three_surface_sqs->opacity = 1.f;
-  auto* child_three_surface_quad =
-      child_one_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_three_surface_quad->SetNew(
-      child_three_surface_sqs, gfx::Rect(kSurfaceSize), gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_three_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-
+  auto child_one_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_three_surface_id))
+          .Build();
   QueuePassAsFrame(std::move(child_one_pass),
                    child_one_surface_id.local_surface_id(), device_scale_factor,
                    child_one_support.get());
@@ -2648,14 +2568,13 @@
   TestSurfaceIdAllocator child_two_surface_id(
       child_two_support->frame_sink_id());
 
-  auto child_two_pass = CompositorRenderPass::Create();
-  child_two_pass->SetNew(pass_id, output_rect, damage_rect,
-                         transform_to_root_target);
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_two_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_two_pass.get(),
-                                 SkBlendMode::kSrcOver,
-                                 kMaskFilterInfoWithRoundedCorners);
+  auto child_two_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .SetMaskFilter(kMaskFilterInfoWithRoundedCorners,
+                         /*is_fast_rounded_corner=*/false)
+          .Build();
   QueuePassAsFrame(std::move(child_two_pass),
                    child_two_surface_id.local_surface_id(), device_scale_factor,
                    child_two_support.get());
@@ -2664,55 +2583,25 @@
   TestSurfaceIdAllocator child_root_surface_id(
       child_root_support->frame_sink_id());
 
-  auto child_root_pass = CompositorRenderPass::Create();
-  child_root_pass->SetNew(pass_id, output_rect, damage_rect,
-                          transform_to_root_target);
-
-  // Add child one surface quad
-  auto* child_one_surface_sqs =
-      child_root_pass->CreateAndAppendSharedQuadState();
-  child_one_surface_sqs->opacity = 1.f;
-  auto* child_one_surface_quad =
-      child_root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_one_surface_quad->SetNew(
-      child_one_surface_sqs, gfx::Rect(kSurfaceSize), gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_one_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-
-  // Add child two surface quad
-  auto* child_two_surface_sqs =
-      child_root_pass->CreateAndAppendSharedQuadState();
-  child_two_surface_sqs->opacity = 1.f;
-  auto* child_two_surface_quad =
-      child_root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_two_surface_quad->SetNew(
-      child_two_surface_sqs, gfx::Rect(kSurfaceSize), gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_two_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-
-  // Add solid color quad
-  AddSolidColorQuadWithBlendMode(kSurfaceSize, child_root_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
+  auto child_root_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_one_surface_id))
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_two_surface_id))
+          .AddSolidColorQuad(gfx::Rect(kSurfaceSize), SK_ColorGREEN)
+          .Build();
   QueuePassAsFrame(std::move(child_root_pass),
                    child_root_surface_id.local_surface_id(),
                    device_scale_factor, child_root_support.get());
 
-  auto root_pass = CompositorRenderPass::Create();
-  root_pass->SetNew(pass_id, output_rect, damage_rect,
-                    transform_to_root_target);
-
-  auto* child_root_surface_sqs = root_pass->CreateAndAppendSharedQuadState();
-  auto* child_root_surface_quad =
-      root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
-  child_root_surface_sqs->opacity = 1.f;
-  child_root_surface_sqs->mask_filter_info =
-      kMaskFilterInfoWithFastRoundedCorners;
-  child_root_surface_sqs->is_fast_rounded_corner = true;
-  child_root_surface_quad->SetNew(
-      child_root_surface_sqs, gfx::Rect(kSurfaceSize), gfx::Rect(kSurfaceSize),
-      SurfaceRange(absl::nullopt, child_root_surface_id), SK_ColorWHITE,
-      /*stretch_content_to_fill_bounds=*/false);
-
+  auto root_pass =
+      RenderPassBuilder(kSurfaceSize)
+          .AddSurfaceQuad(gfx::Rect(kSurfaceSize),
+                          SurfaceRange(absl::nullopt, child_root_surface_id))
+          .SetMaskFilter(kMaskFilterInfoWithFastRoundedCorners,
+                         /*is_fast_rounded_corner=*/true)
+          .Build();
   QueuePassAsFrame(std::move(root_pass), root_surface_id_.local_surface_id(),
                    device_scale_factor, root_sink_.get());
 
@@ -2722,7 +2611,7 @@
 
   // There should be 2 render passes since one of the surface quads could reject
   // merging due to it having a quad with a rounded corner of its own.
-  EXPECT_EQ(2u, aggregated_pass_list.size());
+  ASSERT_EQ(2u, aggregated_pass_list.size());
 
   // The surface quad which has a render pass of its own, will have 2 quads.
   // One of them will have the rounded corner set on it.
@@ -3597,19 +3486,30 @@
   LocalSurfaceId id5 = child_allocator.GetCurrentLocalSurfaceId();
   SurfaceId fallback_surface_id(kArbitraryFrameSinkId1, id2);
   SurfaceId primary_surface_id(kArbitraryFrameSinkId1, id4);
-  std::vector<Quad> embedded_quads = {
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
-  std::vector<Pass> embedded_passes = {Pass(embedded_quads, kSurfaceSize)};
 
-  constexpr float device_scale_factor = 1.0f;
-  SubmitCompositorFrame(embedded_support.get(), embedded_passes, id2,
-                        device_scale_factor);
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLUE))
+            .Build();
+    embedded_support->SubmitCompositorFrame(id2, std::move(frame));
+  }
 
-  CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
-      {SurfaceRange(fallback_surface_id, primary_surface_id)});
-  root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
-                                    std::move(frame));
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSurfaceQuad(
+                        gfx::Rect(5, 5),
+                        SurfaceRange(fallback_surface_id, primary_surface_id)))
+            .Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                      std::move(frame));
+  }
 
   auto aggregated_frame = AggregateFrame(root_surface_id_);
 
@@ -3657,19 +3557,30 @@
   LocalSurfaceId id4 = sink2_allocator.GetCurrentLocalSurfaceId();
   SurfaceId fallback_surface_id(kArbitraryFrameSinkId1, id2);
   SurfaceId primary_surface_id(kArbitraryFrameSinkId2, id4);
-  std::vector<Quad> embedded_quads = {
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
-  std::vector<Pass> embedded_passes = {Pass(embedded_quads, kSurfaceSize)};
 
-  constexpr float device_scale_factor = 1.0f;
-  SubmitCompositorFrame(embedded_support.get(), embedded_passes, id2,
-                        device_scale_factor);
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLUE))
+            .Build();
+    embedded_support->SubmitCompositorFrame(id2, std::move(frame));
+  }
 
-  CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
-      {SurfaceRange(fallback_surface_id, primary_surface_id)});
-  root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
-                                    std::move(frame));
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSurfaceQuad(
+                        gfx::Rect(5, 5),
+                        SurfaceRange(fallback_surface_id, primary_surface_id)))
+            .Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                      std::move(frame));
+  }
 
   auto aggregated_frame = AggregateFrame(root_surface_id_);
 
@@ -3711,8 +3622,13 @@
   allocator.GenerateId();
   LocalSurfaceId id4 = allocator2.GetCurrentLocalSurfaceId();
 
-  CompositorFrame frame = MakeCompositorFrameFromSurfaceRanges(
-      {SurfaceRange(absl::nullopt, primary_surface_id)});
+  CompositorFrame frame =
+      CompositorFrameBuilder()
+          .AddRenderPass(RenderPassBuilder(kSurfaceSize)
+                             .AddSurfaceQuad(gfx::Rect(5, 5),
+                                             SurfaceRange(absl::nullopt,
+                                                          primary_surface_id)))
+          .Build();
   root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
                                     std::move(frame));
 
@@ -3754,18 +3670,27 @@
   allocator2.GenerateId();
   LocalSurfaceId id4 = allocator2.GetCurrentLocalSurfaceId();
 
-  std::vector<Quad> embedded_quads = {
-      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(5, 5)),
-      Quad::SolidColorQuad(SK_ColorBLUE, gfx::Rect(5, 5))};
-  std::vector<Pass> embedded_passes = {Pass(embedded_quads, kSurfaceSize)};
-  constexpr float device_scale_factor = 1.0f;
-  SubmitCompositorFrame(embedded_support.get(), embedded_passes, id2,
-                        device_scale_factor);
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN)
+                    .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorBLUE))
+            .Build();
+    embedded_support->SubmitCompositorFrame(id2, std::move(frame));
+  }
 
-  CompositorFrame frame =
-      MakeCompositorFrameFromSurfaceRanges({SurfaceRange(surface_id)});
-  root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
-                                    std::move(frame));
+  {
+    CompositorFrame frame =
+        CompositorFrameBuilder()
+            .AddRenderPass(
+                RenderPassBuilder(kSurfaceSize)
+                    .AddSurfaceQuad(gfx::Rect(5, 5), SurfaceRange(surface_id)))
+            .Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id_.local_surface_id(),
+                                      std::move(frame));
+  }
 
   auto aggregated_frame = AggregateFrame(root_surface_id_);
 
@@ -6374,8 +6299,7 @@
     CompositorFrame grandchild_frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(kSurfaceSize)
                     .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN))
             .Build();
     grand_child_sink->SubmitCompositorFrame(
@@ -6384,23 +6308,21 @@
     CompositorFrame child_frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(CompositorRenderPassId{1}, kSurfaceSize)
                     .AddSurfaceQuad(gfx::Rect(5, 5),
                                     SurfaceRange(grand_child_surface_id),
                                     {.allow_merge = false}))
-            .AddRenderPass(RenderPassBuilder(CompositorRenderPassId{2},
-                                             gfx::Rect(kSurfaceSize))
-                               .AddRenderPassQuad(gfx::Rect(kSurfaceSize),
-                                                  CompositorRenderPassId{1}))
+            .AddRenderPass(
+                RenderPassBuilder(CompositorRenderPassId{2}, kSurfaceSize)
+                    .AddRenderPassQuad(gfx::Rect(kSurfaceSize),
+                                       CompositorRenderPassId{1}))
             .Build();
     child_sink_->SubmitCompositorFrame(child_surface_id.local_surface_id(),
                                        std::move(child_frame));
 
     CompositorFrame root_frame =
         CompositorFrameBuilder()
-            .AddRenderPass(RenderPassBuilder(CompositorRenderPassId{1},
-                                             gfx::Rect(kSurfaceSize))
+            .AddRenderPass(RenderPassBuilder(kSurfaceSize)
                                .AddSurfaceQuad(gfx::Rect(5, 5),
                                                SurfaceRange(child_surface_id),
                                                {.allow_merge = false}))
@@ -6427,8 +6349,7 @@
     CompositorFrame grandchild_frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(kSurfaceSize)
                     .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN))
             .Build();
     grand_child_sink->SubmitCompositorFrame(
@@ -6455,16 +6376,14 @@
     CompositorFrame child_frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(kSurfaceSize)
                     .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN))
             .Build();
     child_sink_->SubmitCompositorFrame(child_surface_id.local_surface_id(),
                                        std::move(child_frame));
     CompositorFrame root_frame =
         CompositorFrameBuilder()
-            .AddRenderPass(RenderPassBuilder(CompositorRenderPassId{1},
-                                             gfx::Rect(kSurfaceSize))
+            .AddRenderPass(RenderPassBuilder(kSurfaceSize)
                                .AddSurfaceQuad(gfx::Rect(5, 5),
                                                SurfaceRange(child_surface_id),
                                                {.allow_merge = false}))
@@ -6492,8 +6411,7 @@
     CompositorFrame child_frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1},
-                                  gfx::Rect(kSurfaceSize))
+                RenderPassBuilder(kSurfaceSize)
                     .AddSolidColorQuad(gfx::Rect(5, 5), SK_ColorGREEN)
                     .SetHasDamageFromContributingContent(true)
                     .SetDamageRect(gfx::Rect()))
@@ -7493,7 +7411,7 @@
     CompositorFrame frame =
         CompositorFrameBuilder()
             .AddRenderPass(
-                RenderPassBuilder(CompositorRenderPassId{1}, child_surface_rect)
+                RenderPassBuilder(child_surface_rect)
                     .AddSolidColorQuad(child_surface_rect, SK_ColorGREEN))
             .Build();
     child_sink_->SubmitCompositorFrame(child_surface_id.local_surface_id(),
diff --git a/components/viz/test/compositor_frame_helpers.cc b/components/viz/test/compositor_frame_helpers.cc
index ccbafee0..2d8592a9 100644
--- a/components/viz/test/compositor_frame_helpers.cc
+++ b/components/viz/test/compositor_frame_helpers.cc
@@ -229,6 +229,20 @@
   return *this;
 }
 
+RenderPassBuilder& RenderPassBuilder::SetBlendMode(SkBlendMode blend_mode) {
+  GetLastQuadSharedQuadState()->blend_mode = blend_mode;
+  return *this;
+}
+
+RenderPassBuilder& RenderPassBuilder::SetMaskFilter(
+    const gfx::MaskFilterInfo& mask_filter_info,
+    bool is_fast_rounded_corner) {
+  auto* sqs = GetLastQuadSharedQuadState();
+  sqs->mask_filter_info = mask_filter_info;
+  sqs->is_fast_rounded_corner = is_fast_rounded_corner;
+  return *this;
+}
+
 SharedQuadState* RenderPassBuilder::AppendDefaultSharedQuadState(
     const gfx::Rect rect,
     const gfx::Rect visible_rect) {
@@ -412,6 +426,11 @@
   return CompositorFrameBuilder().AddDefaultRenderPass().Build();
 }
 
+CompositorFrame MakeCompositorFrame(
+    std::unique_ptr<CompositorRenderPass> render_pass) {
+  return CompositorFrameBuilder().AddRenderPass(std::move(render_pass)).Build();
+}
+
 CompositorFrame MakeCompositorFrame(CompositorRenderPassList render_pass_list) {
   return CompositorFrameBuilder()
       .SetRenderPassList(std::move(render_pass_list))
diff --git a/components/viz/test/compositor_frame_helpers.h b/components/viz/test/compositor_frame_helpers.h
index db9457a1..a2e6141 100644
--- a/components/viz/test/compositor_frame_helpers.h
+++ b/components/viz/test/compositor_frame_helpers.h
@@ -16,6 +16,7 @@
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/skia/include/core/SkBlendMode.h"
 #include "ui/latency/latency_info.h"
 
 namespace viz {
@@ -46,7 +47,7 @@
   bool needs_blending = false;
   bool premultiplied_alpha = false;
   SkColor background_color = SK_ColorGREEN;
-  float vertex_opacity[4] = {0.f, 0.f, 1.f, 1.f};
+  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   bool flipped = false;
   bool nearest_neighbor = false;
   bool secure_output_only = false;
@@ -149,6 +150,14 @@
   // Sets SharedQuadState::clip_rect for the last quad.
   RenderPassBuilder& SetQuadClipRect(absl::optional<gfx::Rect> clip_rect);
 
+  // Sets SharedQuadState::blend_mode for the last quad.
+  RenderPassBuilder& SetBlendMode(SkBlendMode blend_mode);
+
+  // Sets SharedQuadState::mask_filter_info and
+  // SharedQuadState::is_fast_rounded_corner for the last quad.
+  RenderPassBuilder& SetMaskFilter(const gfx::MaskFilterInfo& mask_filter_info,
+                                   bool is_fast_rounded_corner);
+
  private:
   // Appends and returns a new SharedQuadState for quad.
   SharedQuadState* AppendDefaultSharedQuadState(const gfx::Rect rect,
@@ -235,6 +244,10 @@
 // empty damage_rect. This CompositorFrame is valid and can be sent over IPC.
 CompositorFrame MakeDefaultCompositorFrame();
 
+// Creates a CompositorFrame with provided render pass.
+CompositorFrame MakeCompositorFrame(
+    std::unique_ptr<CompositorRenderPass> render_pass);
+
 // Creates a CompositorFrame with provided list of render passes.
 CompositorFrame MakeCompositorFrame(CompositorRenderPassList render_pass_list);
 
diff --git a/components/viz/test/draw_quad_matchers.cc b/components/viz/test/draw_quad_matchers.cc
index 6bf8ce0..11660a2 100644
--- a/components/viz/test/draw_quad_matchers.cc
+++ b/components/viz/test/draw_quad_matchers.cc
@@ -4,19 +4,62 @@
 
 #include "components/viz/test/draw_quad_matchers.h"
 
+#include "components/viz/common/quads/shared_quad_state.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 
 namespace viz {
 namespace {
 
+const char* MaterialToString(DrawQuad::Material material) {
+  switch (material) {
+    case DrawQuad::Material::kInvalid:
+      return "kInvalid";
+    case DrawQuad::Material::kDebugBorder:
+      return "kDebugBorder";
+    case DrawQuad::Material::kPictureContent:
+      return "kPictureContent";
+    case DrawQuad::Material::kCompositorRenderPass:
+      return "kCompositorRenderPass";
+    case DrawQuad::Material::kAggregatedRenderPass:
+      return "kAggregatedRenderPass";
+    case DrawQuad::Material::kSolidColor:
+      return "kSolidColor";
+    case DrawQuad::Material::kSharedElement:
+      return "kSharedElement";
+    case DrawQuad::Material::kStreamVideoContent:
+      return "kStreamVideoContent";
+    case DrawQuad::Material::kSurfaceContent:
+      return "kSurfaceContent";
+    case DrawQuad::Material::kTextureContent:
+      return "kTextureContent";
+    case DrawQuad::Material::kTiledContent:
+      return "kTiledContent";
+    case DrawQuad::Material::kYuvVideoContent:
+      return "kYuvVideoContent";
+    case DrawQuad::Material::kVideoHole:
+      return "kVideoHole";
+  }
+}
+
 // Produces a matcher for a DrawQuad that matches on quad material.
 testing::Matcher<const DrawQuad*> IsQuadType(
     DrawQuad::Material expected_material) {
-  return testing::Field("material", &DrawQuad::material, expected_material);
+  return testing::Field("material", &DrawQuad::material,
+                        testing::Eq(expected_material));
+}
+
+testing::Matcher<const DrawQuad*> HasSharedQuadState(
+    testing::Matcher<const SharedQuadState*> matcher) {
+  return testing::Field("shared_quad_state", &DrawQuad::shared_quad_state,
+                        matcher);
 }
 
 }  // namespace
 
+void PrintTo(DrawQuad::Material material, ::std::ostream* os) {
+  *os << MaterialToString(material);
+}
+
 testing::Matcher<const DrawQuad*> IsSolidColorQuad() {
   return IsQuadType(DrawQuad::Material::kSolidColor);
 }
@@ -37,8 +80,29 @@
   return IsQuadType(DrawQuad::Material::kYuvVideoContent);
 }
 
+testing::Matcher<const DrawQuad*> IsSurfaceQuad() {
+  return IsQuadType(DrawQuad::Material::kSurfaceContent);
+}
+
 testing::Matcher<const DrawQuad*> IsAggregatedRenderPassQuad() {
   return IsQuadType(DrawQuad::Material::kAggregatedRenderPass);
 }
 
+testing::Matcher<const DrawQuad*> HasRect(const gfx::Rect& rect) {
+  return testing::Field("rect", &DrawQuad::rect, testing::Eq(rect));
+}
+
+testing::Matcher<const DrawQuad*> HasVisibleRect(
+    const gfx::Rect& visible_rect) {
+  return testing::Field("visible_rect", &DrawQuad::visible_rect,
+                        testing::Eq(visible_rect));
+}
+
+testing::Matcher<const DrawQuad*> HasTransform(
+    const gfx::Transform& transform) {
+  return HasSharedQuadState(testing::Field(
+      "quad_to_target_transform", &SharedQuadState::quad_to_target_transform,
+      testing::Eq(transform)));
+}
+
 }  // namespace viz
diff --git a/components/viz/test/draw_quad_matchers.h b/components/viz/test/draw_quad_matchers.h
index 65c37cd7a..2cb973c 100644
--- a/components/viz/test/draw_quad_matchers.h
+++ b/components/viz/test/draw_quad_matchers.h
@@ -27,21 +27,36 @@
 
 namespace viz {
 
-// Produces a matcher that matches a SolidColorDrawQuad.
+// Provides human readable quad material names for gtest/gmock.
+void PrintTo(DrawQuad::Material material, ::std::ostream* os);
+
+// Matches a SolidColorDrawQuad.
 testing::Matcher<const DrawQuad*> IsSolidColorQuad();
 
-// Produces a matcher that matches a SolidColorDrawQuad with |expected_color|.
+// Matches a SolidColorDrawQuad with |expected_color|.
 testing::Matcher<const DrawQuad*> IsSolidColorQuad(SkColor expected_color);
 
-// Produces a matcher that matches a TextureDrawQuad.
+// Matches a TextureDrawQuad.
 testing::Matcher<const DrawQuad*> IsTextureQuad();
 
-// Produces a matcher that matches a YuvVideoDrawQuad.
+// Matches a YuvVideoDrawQuad.
 testing::Matcher<const DrawQuad*> IsYuvVideoQuad();
 
-// Produces a matcher that matches a AggregatedRenderPassQuad.
+// Matches a SurfaceDrawQuad.
+testing::Matcher<const DrawQuad*> IsSurfaceQuad();
+
+// Matches an AggregatedRenderPassQuad.
 testing::Matcher<const DrawQuad*> IsAggregatedRenderPassQuad();
 
+// Matches a DrawQuad with expected DrawQuad::rect.
+testing::Matcher<const DrawQuad*> HasRect(const gfx::Rect& rect);
+
+// Matches a DrawQuad with expected DrawQuad::visible_rect.
+testing::Matcher<const DrawQuad*> HasVisibleRect(const gfx::Rect& visible_rect);
+
+// Matches a DrawQuad with expected SharedQuadState::quad_to_target_transform.
+testing::Matcher<const DrawQuad*> HasTransform(const gfx::Transform& transform);
+
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_TEST_DRAW_QUAD_MATCHERS_H_
diff --git a/components/webauthn/android/BUILD.gn b/components/webauthn/android/BUILD.gn
index c661289..7175fb7f 100644
--- a/components/webauthn/android/BUILD.gn
+++ b/components/webauthn/android/BUILD.gn
@@ -22,6 +22,7 @@
     "java/src/org/chromium/components/webauthn/GetAssertionResponseCallback.java",
     "java/src/org/chromium/components/webauthn/InternalAuthenticator.java",
     "java/src/org/chromium/components/webauthn/IsUvpaaResponseCallback.java",
+    "java/src/org/chromium/components/webauthn/ListCredentials.java",
     "java/src/org/chromium/components/webauthn/MakeCredentialResponseCallback.java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/ListCredentials.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/ListCredentials.java
new file mode 100644
index 0000000..7ab221c
--- /dev/null
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/ListCredentials.java
@@ -0,0 +1,82 @@
+// 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.
+
+package org.chromium.components.webauthn;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Wrapper for an external call to obtain information about discoverable credentials.
+ *
+ * <p>Provides a static instance of an interface that can be set by an external caller to implement
+ * the listCredentials method. That method provides silent enumeration of discoverable platform
+ * credentials for a given relying party, in support of WebAuthn Conditional UI.
+ */
+public class ListCredentials {
+    /**
+     * Describes a discoverable credential available on the device.
+     */
+    public class DiscoverableCredentialInfo {
+        private String mUserName;
+        private String mUserDisplayName;
+        private byte[] mUserId;
+        private byte[] mCredentialId;
+
+        DiscoverableCredentialInfo(
+                String userName, String userDisplayName, byte[] userId, byte[] credentialId) {
+            mUserName = userName;
+            mUserDisplayName = userDisplayName;
+            mUserId = userId;
+            mCredentialId = credentialId;
+        }
+
+        String getUserName() {
+            return mUserName;
+        }
+
+        String getUserDisplayName() {
+            return mUserDisplayName;
+        }
+
+        byte[] getUserId() {
+            return mUserId;
+        }
+
+        byte[] getCredentialId() {
+            return mCredentialId;
+        }
+    }
+
+    /**
+     * Abstract interface for an embedder to provide a listCredentials method.
+     */
+    public interface AbstractListCredentials {
+        List<DiscoverableCredentialInfo> listCredentials(String rpId);
+    }
+
+    private static AbstractListCredentials sExternalListCredentials;
+
+    /**
+     * Sets an interface that will provide a listCredentials method implementation.
+     *
+     * @param listCredentials the external interface.
+     */
+    public static void setExternalListCredentials(AbstractListCredentials listCredentials) {
+        sExternalListCredentials = listCredentials;
+    }
+
+    /**
+     * Returns a list of discoverable credentials available on this design.
+     *
+     * @param rpId indicates the relying party for which this will return any available
+     * discoverable credentials.
+     */
+    public static List<DiscoverableCredentialInfo> listCredentials(String rpId) {
+        if (sExternalListCredentials != null) {
+            return sExternalListCredentials.listCredentials(rpId);
+        }
+        return new ArrayList<DiscoverableCredentialInfo>();
+    }
+}
diff --git a/content/browser/aggregation_service/aggregatable_report.cc b/content/browser/aggregation_service/aggregatable_report.cc
index 59470d9..e315a7e8 100644
--- a/content/browser/aggregation_service/aggregatable_report.cc
+++ b/content/browser/aggregation_service/aggregatable_report.cc
@@ -48,7 +48,7 @@
 constexpr char kVersionValue[] = "";
 
 // Payload contents:
-constexpr char kHierarchicalHistogramValue[] = "hierarchical-histogram";
+constexpr char kHistogramValue[] = "histogram";
 constexpr char kOperationKey[] = "operation";
 constexpr char kReportingOriginKey[] = "reporting_origin";
 
@@ -71,9 +71,8 @@
 // Returns empty vector in case of error.
 std::vector<DpfKey> GenerateDpfKeys(
     const AggregationServicePayloadContents& contents) {
-  DCHECK_EQ(
-      contents.operation,
-      AggregationServicePayloadContents::Operation::kHierarchicalHistogram);
+  DCHECK_EQ(contents.operation,
+            AggregationServicePayloadContents::Operation::kHistogram);
   DCHECK_EQ(contents.processing_type,
             AggregationServicePayloadContents::ProcessingType::kTwoParty);
 
@@ -147,7 +146,7 @@
 
     value.emplace(kReportingOriginKey,
                   payload_contents.reporting_origin.Serialize());
-    value.emplace(kOperationKey, kHierarchicalHistogramValue);
+    value.emplace(kOperationKey, kHistogramValue);
     value.emplace("dpf_key", std::move(serialized_key));
 
     absl::optional<std::vector<uint8_t>> unencrypted_payload =
@@ -180,12 +179,15 @@
 
     value.emplace(kReportingOriginKey,
                   payload_contents.reporting_origin.Serialize());
-    value.emplace(kOperationKey, kHierarchicalHistogramValue);
+    value.emplace(kOperationKey, kHistogramValue);
 
-    cbor::Value::MapValue data;
+    // TODO(crbug.com/1272030): Support multiple contributions in one payload.
+    cbor::Value::ArrayValue data;
     if (i == index_to_populate) {
-      data.emplace("bucket", payload_contents.bucket);
-      data.emplace("value", payload_contents.value);
+      cbor::Value::MapValue data_map;
+      data_map.emplace("bucket", payload_contents.bucket);
+      data_map.emplace("value", payload_contents.value);
+      data.push_back(cbor::Value(std::move(data_map)));
     }
     value.emplace("data", std::move(data));
 
diff --git a/content/browser/aggregation_service/aggregatable_report.h b/content/browser/aggregation_service/aggregatable_report.h
index 16c24754..a5418c9 100644
--- a/content/browser/aggregation_service/aggregatable_report.h
+++ b/content/browser/aggregation_service/aggregatable_report.h
@@ -30,8 +30,8 @@
 struct CONTENT_EXPORT AggregationServicePayloadContents {
   // TODO(alexmt): Add kDistinctCount option.
   enum class Operation {
-    kHierarchicalHistogram = 0,
-    kMaxValue = kHierarchicalHistogram,
+    kHistogram = 0,
+    kMaxValue = kHistogram,
   };
 
   enum class ProcessingType {
@@ -94,10 +94,10 @@
     // }
     // For the kSingleServer processing type, the "dpf_key" field is replaced
     // with:
-    //   "data": { "bucket": <bucket>, "value": <value> }
+    //   "data": [{ "bucket": <bucket>, "value": <value> }]
     // If two processing origins are provided, one payload (chosen randomly)
     // would contain that data and the other would instead contain:
-    //   "data": {}
+    //   "data": []
     std::vector<uint8_t> payload;
 
     // Indicates the chosen encryption key.
diff --git a/content/browser/aggregation_service/aggregatable_report_assembler.cc b/content/browser/aggregation_service/aggregatable_report_assembler.cc
index ada4ed27..d26ec6d 100644
--- a/content/browser/aggregation_service/aggregatable_report_assembler.cc
+++ b/content/browser/aggregation_service/aggregatable_report_assembler.cc
@@ -110,9 +110,8 @@
       report_request.payload_contents();
 
   // Currently, this is the only supported operation.
-  DCHECK_EQ(
-      contents.operation,
-      AggregationServicePayloadContents::Operation::kHierarchicalHistogram);
+  DCHECK_EQ(contents.operation,
+            AggregationServicePayloadContents::Operation::kHistogram);
 
   if (pending_requests_.size() >= kMaxSimultaneousRequests) {
     std::move(callback).Run(absl::nullopt,
diff --git a/content/browser/aggregation_service/aggregatable_report_unittest.cc b/content/browser/aggregation_service/aggregatable_report_unittest.cc
index b23599c..b0046d4 100644
--- a/content/browser/aggregation_service/aggregatable_report_unittest.cc
+++ b/content/browser/aggregation_service/aggregatable_report_unittest.cc
@@ -144,7 +144,7 @@
     ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "operation",
                                           cbor::Value::Type::STRING));
     EXPECT_EQ(payload_map.at(cbor::Value("operation")).GetString(),
-              "hierarchical-histogram");
+              "histogram");
 
     switch (expected_payload_contents.processing_type) {
       case AggregationServicePayloadContents::ProcessingType::kTwoParty: {
@@ -159,13 +159,19 @@
       }
       case AggregationServicePayloadContents::ProcessingType::kSingleServer: {
         ASSERT_TRUE(CborMapContainsKeyAndType(payload_map, "data",
-                                              cbor::Value::Type::MAP));
-        const cbor::Value::MapValue& data_map =
-            payload_map.at(cbor::Value("data")).GetMap();
+                                              cbor::Value::Type::ARRAY));
+        const cbor::Value::ArrayValue& data_array =
+            payload_map.at(cbor::Value("data")).GetArray();
 
-        if (!data_map.empty()) {
+        if (!data_array.empty()) {
           ++complete_data_count;
 
+          // TODO(crbug.com/1272030): Support multiple contributions in one
+          // payload.
+          EXPECT_EQ(data_array.size(), 1u);
+          ASSERT_TRUE(data_array[0].is_map());
+          const cbor::Value::MapValue& data_map = data_array[0].GetMap();
+
           ASSERT_TRUE(CborMapContainsKeyAndType(data_map, "bucket",
                                                 cbor::Value::Type::UNSIGNED));
           EXPECT_EQ(data_map.at(cbor::Value("bucket")).GetInteger(),
diff --git a/content/browser/aggregation_service/aggregation_service_test_utils.cc b/content/browser/aggregation_service/aggregation_service_test_utils.cc
index 4a40fd1..4710979 100644
--- a/content/browser/aggregation_service/aggregation_service_test_utils.cc
+++ b/content/browser/aggregation_service/aggregation_service_test_utils.cc
@@ -175,8 +175,7 @@
   return AggregatableReportRequest::Create(
              std::move(processing_origins),
              AggregationServicePayloadContents(
-                 AggregationServicePayloadContents::Operation::
-                     kHierarchicalHistogram,
+                 AggregationServicePayloadContents::Operation::kHistogram,
                  /*bucket=*/123, /*value=*/456, processing_type,
                  url::Origin::Create(GURL("https://reporting.example"))),
              AggregatableReportSharedInfo(
@@ -301,8 +300,8 @@
     std::ostream& out,
     const AggregationServicePayloadContents::Operation& operation) {
   switch (operation) {
-    case AggregationServicePayloadContents::Operation::kHierarchicalHistogram:
-      return out << "kHierarchicalHistogram";
+    case AggregationServicePayloadContents::Operation::kHistogram:
+      return out << "kHistogram";
   }
 }
 
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index 85a55199..6e18a8c3 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -302,7 +302,7 @@
     devtools_context()->AddObserver(this);
 
     web_contents_ = base::WrapUnique(WebContentsTester::CreateTestWebContents(
-        WebContents::CreateParams(browser_context(), nullptr)));
+        WebContents::CreateParams(browser_context())));
     std::unique_ptr<MockPermissionManager> mock_permission_manager(
         new testing::NiceMock<MockPermissionManager>());
     ON_CALL(*mock_permission_manager,
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index fca5ef2..35e5348 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -281,7 +281,6 @@
   RFH_CREATE_CHILD_FRAME_SANDBOX_FLAGS = 254,
   RFPH_FOCUSED_FENCED_FRAME = 255,
   WCI_REQUEST_LOCK_MOUSE_FENCED_FRAME = 256,
-  RFH_FENCED_FRAME_MOJO_WHEN_DISABLED = 257,
 
   // 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/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index ac7f5d6..cddf902 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -1857,7 +1857,7 @@
   EXPECT_TRUE(notifications_.empty());
 
   WebContents::CreateParams create_params(
-      ShellContentBrowserClient::Get()->browser_context(), nullptr);
+      ShellContentBrowserClient::Get()->browser_context());
   std::unique_ptr<content::WebContents> web_contents(
       content::WebContents::Create(create_params));
   EXPECT_TRUE(notifications_.empty());
diff --git a/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h b/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
index 17df70d..5366ad66 100644
--- a/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
+++ b/content/browser/gpu/gpu_data_manager_testing_arrays_and_structs_autogen.h
@@ -91,8 +91,8 @@
 };
 
 const int kFeatureListForGpuManagerTestingEntry4[2] = {
-    GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
     GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
+    GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
 };
 
 const GpuControlList::GLStrings kGLStringsForGpuManagerTestingEntry4 = {
@@ -171,18 +171,19 @@
     GpuControlList::kDontCare,  // subpixel_font_rendering
 };
 
-const int kFeatureListForGpuManagerTestingEntry6[11] = {
-    GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
-    GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL,
+const int kFeatureListForGpuManagerTestingEntry6[12] = {
+    GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
+    GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
+    GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
+    GPU_FEATURE_TYPE_ACCELERATED_VIDEO_ENCODE,
     GPU_FEATURE_TYPE_GPU_RASTERIZATION,
+    GPU_FEATURE_TYPE_ACCELERATED_WEBGL2,
+    GPU_FEATURE_TYPE_OOP_RASTERIZATION,
+    GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL,
     GPU_FEATURE_TYPE_ACCELERATED_GL,
     GPU_FEATURE_TYPE_METAL,
-    GPU_FEATURE_TYPE_CANVAS_OOP_RASTERIZATION,
-    GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS,
-    GPU_FEATURE_TYPE_OOP_RASTERIZATION,
-    GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE,
-    GPU_FEATURE_TYPE_ACCELERATED_WEBGL,
     GPU_FEATURE_TYPE_VULKAN,
+    GPU_FEATURE_TYPE_CANVAS_OOP_RASTERIZATION,
 };
 
 const GpuControlList::More kMoreForEntry6_572251052 = {
diff --git a/content/browser/media/media_internals_audio_focus_helper.cc b/content/browser/media/media_internals_audio_focus_helper.cc
index 482d282..30ef49f 100644
--- a/content/browser/media/media_internals_audio_focus_helper.cc
+++ b/content/browser/media/media_internals_audio_focus_helper.cc
@@ -150,7 +150,7 @@
   if (!EnsureServiceConnection())
     return;
 
-  audio_focus_data_.Clear();
+  audio_focus_data_.DictClear();
   request_state_.clear();
 
   // We should go backwards through the stack so the top of the stack is
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc
index 96c2d7a..ba8bc8dd 100644
--- a/content/browser/media/media_internals_unittest.cc
+++ b/content/browser/media/media_internals_unittest.cc
@@ -357,7 +357,7 @@
         update_data_.FindKeyOfType("sessions", base::Value::Type::LIST)
             ->Clone();
 
-    update_data_.Clear();
+    update_data_.DictClear();
     run_loop_ = std::make_unique<base::RunLoop>();
     call_count_ = 0;
 
@@ -367,7 +367,7 @@
   void Reset() {
     base::AutoLock auto_lock(lock_);
 
-    update_data_.Clear();
+    update_data_.DictClear();
     run_loop_ = std::make_unique<base::RunLoop>();
     call_count_ = 0;
   }
diff --git a/content/browser/permissions/permission_controller_impl_unittest.cc b/content/browser/permissions/permission_controller_impl_unittest.cc
index c0eb6dea..770dc02 100644
--- a/content/browser/permissions/permission_controller_impl_unittest.cc
+++ b/content/browser/permissions/permission_controller_impl_unittest.cc
@@ -200,7 +200,7 @@
        /*expect_death=*/true}};
 
   auto web_contents = base::WrapUnique(WebContentsTester::CreateTestWebContents(
-      WebContents::CreateParams(browser_context(), nullptr)));
+      WebContents::CreateParams(browser_context())));
   RenderFrameHost* rfh = web_contents->GetMainFrame();
   for (const auto& test_case : kTestCases) {
     // Need to reset overrides for each case to ensure delegation is as
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 17628b9..95a0d30 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -2101,11 +2101,10 @@
       break;
     }
     case blink::MEDIA_DEVICE_UPDATE: {
-      // Fail to change capture source, keep everything unchanged and
+      // Fail to change desktop capture source, keep everything unchanged and
       // bring the previous shared tab to the front.
       for (const auto& device : request->devices) {
-        if (device.type == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE ||
-            device.type == MediaStreamType::DISPLAY_VIDEO_CAPTURE) {
+        if (device.type == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
           DesktopMediaID source = DesktopMediaID::Parse(device.id);
           DCHECK(source.type == DesktopMediaID::TYPE_WEB_CONTENTS);
           GetUIThreadTaskRunner({})->PostTask(
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 34241f2..bdfcd901 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2526,6 +2526,11 @@
   }
 }
 
+std::string RenderFrameHostImpl::ToDebugString() {
+  return "RFHI:" +
+         render_view_host_->GetDelegate()->GetCreatorLocation().ToString();
+}
+
 void RenderFrameHostImpl::AccessibilityPerformAction(
     const ui::AXActionData& action_data) {
   // Don't perform any Accessibility action on an inactive frame.
@@ -6888,12 +6893,6 @@
     mojo::PendingAssociatedReceiver<blink::mojom::FencedFrameOwnerHost>
         pending_receiver,
     CreateFencedFrameCallback callback) {
-  if (!blink::features::IsFencedFramesEnabled() ||
-      !blink::features::IsFencedFramesMPArchBased()) {
-    bad_message::ReceivedBadMessage(
-        GetProcess(), bad_message::RFH_FENCED_FRAME_MOJO_WHEN_DISABLED);
-    return;
-  }
   fenced_frames_.push_back(
       std::make_unique<FencedFrame>(weak_ptr_factory_.GetSafeRef()));
   FencedFrame* fenced_frame = fenced_frames_.back().get();
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 03df11f5..c064204 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -500,6 +500,7 @@
   void OnAssociatedInterfaceRequest(
       const std::string& interface_name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
+  std::string ToDebugString() override;
 
   // BrowserAccessibilityDelegate
   void AccessibilityPerformAction(const ui::AXActionData& data) override;
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 9eca7cd..2f70f61 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -28,6 +28,7 @@
 #include "content/browser/renderer_host/ipc_utils.h"
 #include "content/browser/renderer_host/navigator.h"
 #include "content/browser/renderer_host/render_frame_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
@@ -227,6 +228,11 @@
   return false;
 }
 
+std::string RenderFrameProxyHost::ToDebugString() {
+  return "RFPH:" +
+         GetRenderViewHost()->GetDelegate()->GetCreatorLocation().ToString();
+}
+
 bool RenderFrameProxyHost::InitRenderFrameProxy() {
   DCHECK(!render_frame_proxy_created_);
   // We shouldn't be creating proxies for subframes of frames in
diff --git a/content/browser/renderer_host/render_frame_proxy_host.h b/content/browser/renderer_host/render_frame_proxy_host.h
index f8525da..e3913075 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.h
+++ b/content/browser/renderer_host/render_frame_proxy_host.h
@@ -156,6 +156,7 @@
 
   // IPC::Listener
   bool OnMessageReceived(const IPC::Message& msg) override;
+  std::string ToDebugString() override;
 
   CrossProcessFrameConnector* cross_process_frame_connector() {
     return cross_process_frame_connector_.get();
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index fad378b..3727d99 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2222,9 +2222,14 @@
   if (worker_ref_count_ != 0)
     ret += " wrc=" + base::NumberToString(worker_ref_count_);
 
-  if (!listeners_.IsEmpty())
+  if (!listeners_.IsEmpty()) {
     ret += " lsn=" + base::NumberToString(listeners_.size());
 
+    base::IDMap<IPC::Listener*>::Iterator<IPC::Listener> it(&listeners_);
+    IPC::Listener* example_listener = it.GetCurrentValue();
+    ret += "[" + example_listener->ToDebugString() + "]";
+  }
+
   if (deleting_soon_)
     ret += " ds";
 
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index f4173f9a..ad791939 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -123,6 +123,8 @@
   // default color should be used.
   virtual absl::optional<SkColor> GetBaseBackgroundColor();
 
+  virtual const base::Location& GetCreatorLocation() = 0;
+
  protected:
   virtual ~RenderViewHostDelegate() {}
 };
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index d30ce7f6..2d918fcf 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -782,6 +782,10 @@
   return false;
 }
 
+std::string RenderViewHostImpl::ToDebugString() {
+  return "RVHI:" + delegate_->GetCreatorLocation().ToString();
+}
+
 void RenderViewHostImpl::OnTakeFocus(bool reverse) {
   RenderViewHostDelegateView* view = delegate_->GetDelegateView();
   if (view)
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 0bbf5b0a..c2d024d 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -355,6 +355,7 @@
 
   // IPC::Listener implementation.
   bool OnMessageReceived(const IPC::Message& msg) override;
+  std::string ToDebugString() override;
 
   void RenderViewReady();
 
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
index deb76aa..ec47185b 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_browsertest.cc
@@ -378,8 +378,14 @@
 }
 
 // Validate that OOPIFs receive presentation feedbacks.
+// TODO(crbug.com/1270981): Flaky.
+#if defined(OS_LINUX) || defined(OS_MAC)
+#define MAYBE_PresentationFeedback DISABLED_PresentationFeedback
+#else
+#define MAYBE_PresentationFeedback PresentationFeedback
+#endif
 IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewChildFrameBrowserTest,
-                       PresentationFeedback) {
+                       MAYBE_PresentationFeedback) {
   base::HistogramTester histogram_tester;
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
@@ -411,7 +417,7 @@
     GiveItSomeTime();
   } while (histogram_tester
                .GetTotalCountsForPrefix("Browser.Tabs.TotalSwitchDuration")
-               .size() < 1);
+               .size() != 1);
 }
 
 // Auto-resize is only implemented for Ash and GuestViews. So we need to inject
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index afdc02f..b01bc217 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1854,28 +1854,4 @@
               Optional(HasSubstr("Trust Token params in fenced frame nav")));
 }
 
-// Ensure that we kill the renderer process if we try to create a
-// fenced-frame when the blink::features::kFencedFrames feature is not enabled.
-IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
-                       CreateFencedFrameWhenFeatureDisabled) {
-  GURL foo("http://foo.com/simple_page.html");
-  EXPECT_TRUE(NavigateToURL(shell(), foo));
-  EXPECT_EQ(u"OK", shell()->web_contents()->GetTitle());
-  EXPECT_FALSE(blink::features::IsFencedFramesEnabled());
-
-  RenderFrameHostImpl* compromised_rfh = static_cast<RenderFrameHostImpl*>(
-      shell()->web_contents()->GetMainFrame());
-
-  mojo::PendingAssociatedRemote<blink::mojom::FencedFrameOwnerHost> remote;
-  mojo::PendingAssociatedReceiver<blink::mojom::FencedFrameOwnerHost> receiver;
-  receiver = remote.InitWithNewEndpointAndPassReceiver();
-
-  RenderProcessHostBadIpcMessageWaiter kill_waiter(
-      compromised_rfh->GetProcess());
-  static_cast<mojom::FrameHost*>(compromised_rfh)
-      ->CreateFencedFrame(std::move(receiver), base::NullCallback());
-  EXPECT_EQ(bad_message::RFH_FENCED_FRAME_MOJO_WHEN_DISABLED,
-            kill_waiter.Wait());
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_container_host.h b/content/browser/service_worker/service_worker_container_host.h
index 0b13e33..f7124be 100644
--- a/content/browser/service_worker/service_worker_container_host.h
+++ b/content/browser/service_worker/service_worker_container_host.h
@@ -34,7 +34,7 @@
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 
 namespace content {
 
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 59f234d..856a240 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -30,7 +30,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 
 class GURL;
 
diff --git a/content/browser/service_worker/service_worker_job_coordinator.h b/content/browser/service_worker/service_worker_job_coordinator.h
index 8faf102..091d348 100644
--- a/content/browser/service_worker/service_worker_job_coordinator.h
+++ b/content/browser/service_worker/service_worker_job_coordinator.h
@@ -16,7 +16,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace content {
diff --git a/content/browser/service_worker/service_worker_register_job.h b/content/browser/service_worker/service_worker_register_job.h
index c14d6f2..09558c70 100644
--- a/content/browser/service_worker/service_worker_register_job.h
+++ b/content/browser/service_worker/service_worker_register_job.h
@@ -19,7 +19,7 @@
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 #include "url/gurl.h"
 
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index b68151c..a7c5e8ea 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -12,7 +12,7 @@
 #include "services/network/public/cpp/cross_origin_resource_policy.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom-forward.h"
 
 namespace network {
 class MojoToNetPendingBuffer;
diff --git a/content/browser/web_contents/opened_by_dom_browsertest.cc b/content/browser/web_contents/opened_by_dom_browsertest.cc
index ae7e28d..57e1969 100644
--- a/content/browser/web_contents/opened_by_dom_browsertest.cc
+++ b/content/browser/web_contents/opened_by_dom_browsertest.cc
@@ -23,7 +23,7 @@
 // requested it.
 class CloseTrackingDelegate : public WebContentsDelegate {
  public:
-  CloseTrackingDelegate() : close_contents_called_(false) {}
+  CloseTrackingDelegate() = default;
 
   CloseTrackingDelegate(const CloseTrackingDelegate&) = delete;
   CloseTrackingDelegate& operator=(const CloseTrackingDelegate&) = delete;
@@ -35,7 +35,7 @@
   }
 
  private:
-  bool close_contents_called_;
+  bool close_contents_called_ = false;
 };
 
 }  // namespace
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index ff1d6e2..1e0a0c29 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -127,7 +127,7 @@
 
 class WebContentsImplBrowserTest : public ContentBrowserTest {
  public:
-  WebContentsImplBrowserTest() {}
+  WebContentsImplBrowserTest() = default;
   void SetUp() override {
     RenderWidgetHostImpl::DisableResizeAckCheckForTesting();
     ContentBrowserTest::SetUp();
@@ -986,7 +986,7 @@
   const std::vector<double>& progresses = delegate->progresses;
   // All updates should be in order ...
   if (std::adjacent_find(progresses.begin(), progresses.end(),
-                         std::greater<double>()) != progresses.end()) {
+                         std::greater<>()) != progresses.end()) {
     ADD_FAILURE() << "Progress values should be in order: "
                   << ::testing::PrintToString(progresses);
   }
@@ -1007,7 +1007,7 @@
   const std::vector<double>& progresses = delegate->progresses;
   // All updates should be in order ...
   if (std::adjacent_find(progresses.begin(), progresses.end(),
-                         std::greater<double>()) != progresses.end()) {
+                         std::greater<>()) != progresses.end()) {
     ADD_FAILURE() << "Progress values should be in order: "
                   << ::testing::PrintToString(progresses);
   }
@@ -3671,7 +3671,7 @@
   // Create a WebContents detached from native windows so that visibility of
   // the WebContents is fully controlled by the app.
   WebContents::CreateParams create_params(
-      attached_web_contents->GetBrowserContext(), nullptr /* site_instance */);
+      attached_web_contents->GetBrowserContext());
   std::unique_ptr<WebContents> web_contents =
       WebContents::Create(create_params);
   EXPECT_EQ(Visibility::VISIBLE, web_contents->GetVisibility());
@@ -3772,7 +3772,7 @@
   WebContents* attached_web_contents = shell()->web_contents();
 
   WebContents::CreateParams create_params(
-      attached_web_contents->GetBrowserContext(), /*site_instance=*/nullptr);
+      attached_web_contents->GetBrowserContext());
   std::unique_ptr<WebContents> public_web_contents =
       WebContents::Create(create_params);
   auto* web_contents = static_cast<WebContentsImpl*>(public_web_contents.get());
@@ -4923,7 +4923,7 @@
 
 class RenderFrameCreatedObserver : public WebContentsObserver {
  public:
-  RenderFrameCreatedObserver(WebContents* web_contents)
+  explicit RenderFrameCreatedObserver(WebContents* web_contents)
       : WebContentsObserver(web_contents) {}
   ~RenderFrameCreatedObserver() override = default;
 
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 4f02748..04dde6d 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -263,7 +263,7 @@
   FakeFullscreenDelegate(const FakeFullscreenDelegate&) = delete;
   FakeFullscreenDelegate& operator=(const FakeFullscreenDelegate&) = delete;
 
-  ~FakeFullscreenDelegate() override {}
+  ~FakeFullscreenDelegate() override = default;
 
   void EnterFullscreenModeForTab(
       RenderFrameHost* requesting_frame,
@@ -285,12 +285,12 @@
 
 class FakeWebContentsDelegate : public WebContentsDelegate {
  public:
-  FakeWebContentsDelegate() : loading_state_changed_was_called_(false) {}
+  FakeWebContentsDelegate() = default;
 
   FakeWebContentsDelegate(const FakeWebContentsDelegate&) = delete;
   FakeWebContentsDelegate& operator=(const FakeWebContentsDelegate&) = delete;
 
-  ~FakeWebContentsDelegate() override {}
+  ~FakeWebContentsDelegate() override = default;
 
   void LoadingStateChanged(WebContents* source,
                            bool should_show_loading_ui) override {
@@ -302,7 +302,7 @@
   }
 
  private:
-  bool loading_state_changed_was_called_;
+  bool loading_state_changed_was_called_ = false;
 };
 
 class FakeImageDownloader : public blink::mojom::ImageDownloader {
@@ -2111,10 +2111,7 @@
 
 class ContentsZoomChangedDelegate : public WebContentsDelegate {
  public:
-  ContentsZoomChangedDelegate() :
-    contents_zoom_changed_call_count_(0),
-    last_zoom_in_(false) {
-  }
+  ContentsZoomChangedDelegate() = default;
 
   ContentsZoomChangedDelegate(const ContentsZoomChangedDelegate&) = delete;
   ContentsZoomChangedDelegate& operator=(const ContentsZoomChangedDelegate&) =
@@ -2137,8 +2134,8 @@
   }
 
  private:
-  int contents_zoom_changed_call_count_;
-  bool last_zoom_in_;
+  int contents_zoom_changed_call_count_ = 0;
+  bool last_zoom_in_ = false;
 };
 
 // Tests that some mouseehweel events get turned into browser zoom requests.
@@ -2376,7 +2373,7 @@
   LoadingWebContentsObserver& operator=(const LoadingWebContentsObserver&) =
       delete;
 
-  ~LoadingWebContentsObserver() override {}
+  ~LoadingWebContentsObserver() override = default;
 
   // The assertions on these messages ensure that they are received in order.
   void DidStartLoading() override {
@@ -2685,13 +2682,13 @@
 
 class TestJavaScriptDialogManager : public JavaScriptDialogManager {
  public:
-  TestJavaScriptDialogManager() {}
+  TestJavaScriptDialogManager() = default;
 
   TestJavaScriptDialogManager(const TestJavaScriptDialogManager&) = delete;
   TestJavaScriptDialogManager& operator=(const TestJavaScriptDialogManager&) =
       delete;
 
-  ~TestJavaScriptDialogManager() override {}
+  ~TestJavaScriptDialogManager() override = default;
 
   size_t reset_count() { return reset_count_; }
 
diff --git a/content/browser/web_contents/web_contents_observer_browsertest.cc b/content/browser/web_contents/web_contents_observer_browsertest.cc
index d5e21a1..d9e7bf2 100644
--- a/content/browser/web_contents/web_contents_observer_browsertest.cc
+++ b/content/browser/web_contents/web_contents_observer_browsertest.cc
@@ -62,7 +62,7 @@
 
 class ServiceWorkerAccessObserver : public WebContentsObserver {
  public:
-  ServiceWorkerAccessObserver(WebContentsImpl* web_contents)
+  explicit ServiceWorkerAccessObserver(WebContentsImpl* web_contents)
       : WebContentsObserver(web_contents) {}
 
   MOCK_METHOD3(OnServiceWorkerAccessed,
@@ -184,8 +184,8 @@
                               testing::Matcher<NavigationHandle*>(NotNull()),
                               service_worker_scope,
                               AllowServiceWorkerResult::FromPolicy(
-                                  /* javascript_blocked=*/true,
-                                  /* cookies_blocked=*/false)))
+                                  /* javascript_blocked_by_policy=*/true,
+                                  /* cookies_blocked_by_policy=*/false)))
         .WillOnce([&]() { run_loop.Quit(); });
     EXPECT_TRUE(NavigateToURL(
         web_contents(),
@@ -206,8 +206,8 @@
                               testing::Matcher<NavigationHandle*>(NotNull()),
                               service_worker_scope,
                               AllowServiceWorkerResult::FromPolicy(
-                                  /* javascript_blocked=*/false,
-                                  /* cookies_blocked=*/true)))
+                                  /* javascript_blocked_by_policy=*/false,
+                                  /* cookies_blocked_by_policy=*/true)))
         .WillOnce([&]() { run_loop.Quit(); });
     EXPECT_TRUE(NavigateToURL(
         web_contents(),
diff --git a/content/browser/web_contents/web_contents_user_data_unittest.cc b/content/browser/web_contents/web_contents_user_data_unittest.cc
index ed2a4c5..0657701 100644
--- a/content/browser/web_contents/web_contents_user_data_unittest.cc
+++ b/content/browser/web_contents/web_contents_user_data_unittest.cc
@@ -16,7 +16,7 @@
 class WebContentsAttachedClass1
     : public WebContentsUserData<WebContentsAttachedClass1> {
  public:
-  ~WebContentsAttachedClass1() override {}
+  ~WebContentsAttachedClass1() override = default;
 
  private:
   explicit WebContentsAttachedClass1(WebContents* contents)
@@ -30,7 +30,7 @@
 class WebContentsAttachedClass2
     : public WebContentsUserData<WebContentsAttachedClass2> {
  public:
-  ~WebContentsAttachedClass2() override {}
+  ~WebContentsAttachedClass2() override = default;
 
  private:
   explicit WebContentsAttachedClass2(WebContents* contents)
diff --git a/content/browser/web_contents/web_contents_view.h b/content/browser/web_contents/web_contents_view.h
index 0ed6172..dd068f9 100644
--- a/content/browser/web_contents/web_contents_view.h
+++ b/content/browser/web_contents/web_contents_view.h
@@ -23,7 +23,7 @@
 // them.
 class WebContentsView {
  public:
-  virtual ~WebContentsView() {}
+  virtual ~WebContentsView() = default;
 
   // Returns the native widget that contains the contents of the tab.
   virtual gfx::NativeView GetNativeView() const = 0;
diff --git a/content/browser/web_contents/web_contents_view_child_frame.cc b/content/browser/web_contents/web_contents_view_child_frame.cc
index f8c9662d..4796c51 100644
--- a/content/browser/web_contents/web_contents_view_child_frame.cc
+++ b/content/browser/web_contents/web_contents_view_child_frame.cc
@@ -28,7 +28,7 @@
   *delegate_view = this;
 }
 
-WebContentsViewChildFrame::~WebContentsViewChildFrame() {}
+WebContentsViewChildFrame::~WebContentsViewChildFrame() = default;
 
 WebContentsView* WebContentsViewChildFrame::GetOuterView() {
   return web_contents_->GetOuterWebContents()->GetView();
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 4cd507af..22d8188 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -111,7 +111,7 @@
 gfx::NativeView WebContentsViewMac::GetContentNativeView() const {
   RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView();
   if (!rwhv)
-    return NULL;
+    return nullptr;
   return rwhv->GetNativeView();
 }
 
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index 9d2f771..6455404 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -330,7 +330,7 @@
     [self draggingEntered:info];
   }
 
-  _currentRVH = NULL;
+  _currentRVH = nullptr;
   _webContents->Focus();
 
   if (webContentsViewDelegate) {
@@ -438,9 +438,9 @@
         BOOL exists = [[NSFileManager defaultManager]
                            fileExistsAtPath:filename];
         if (exists) {
-          data->filenames.push_back(ui::FileInfo(
+          data->filenames.emplace_back(
               base::FilePath::FromUTF8Unsafe(base::SysNSStringToUTF8(filename)),
-              base::FilePath()));
+              base::FilePath());
         }
       }
     }
diff --git a/content/public/browser/web_contents.cc b/content/public/browser/web_contents.cc
index 3212ed7..cdd2642a 100644
--- a/content/public/browser/web_contents.cc
+++ b/content/public/browser/web_contents.cc
@@ -8,7 +8,6 @@
 
 #include "content/public/common/child_process_host.h"
 #include "ipc/ipc_message.h"
-#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
 
 namespace content {
 
@@ -21,23 +20,10 @@
                                         base::Location creator_location)
     : browser_context(context),
       site_instance(std::move(site)),
-      opener_render_process_id(content::ChildProcessHost::kInvalidUniqueID),
-      opener_render_frame_id(MSG_ROUTING_NONE),
-      opener_suppressed(false),
-      opened_by_another_window(false),
-      initially_hidden(false),
-      guest_delegate(nullptr),
-      context(nullptr),
-      renderer_initiated_creation(false),
-      desired_renderer_state(kOkayToHaveRendererProcess),
-      starting_sandbox_flags(network::mojom::WebSandboxFlags::kNone),
-      is_never_visible(false),
-      creator_location(creator_location),
-      enable_wake_locks(true) {}
+      creator_location(creator_location) {}
 
 WebContents::CreateParams::CreateParams(const CreateParams& other) = default;
 
-WebContents::CreateParams::~CreateParams() {
-}
+WebContents::CreateParams::~CreateParams() = default;
 
 }  // namespace content
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 6114e66..6d1587c1 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -36,6 +36,7 @@
 #include "content/public/browser/visibility.h"
 #include "content/public/common/stop_find_action.h"
 #include "services/data_decoder/public/mojom/web_bundler.mojom.h"
+#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom-forward.h"
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-forward.h"
@@ -128,11 +129,11 @@
     explicit CreateParams(
         BrowserContext* context,
         base::Location creator_location = base::Location::Current());
-    CreateParams(const CreateParams& other);
-    ~CreateParams();
     CreateParams(BrowserContext* context,
                  scoped_refptr<SiteInstance> site,
                  base::Location creator_location = base::Location::Current());
+    CreateParams(const CreateParams& other);
+    ~CreateParams();
 
     raw_ptr<BrowserContext> browser_context;
 
@@ -142,20 +143,20 @@
     scoped_refptr<SiteInstance> site_instance;
 
     // The process id of the frame initiating the open.
-    int opener_render_process_id;
+    int opener_render_process_id = content::ChildProcessHost::kInvalidUniqueID;
 
     // The routing id of the frame initiating the open.
-    int opener_render_frame_id;
+    int opener_render_frame_id = MSG_ROUTING_NONE;
 
     // If the opener is suppressed, then the new WebContents doesn't hold a
     // reference to its opener.
-    bool opener_suppressed;
+    bool opener_suppressed = false;
 
     // Indicates whether this WebContents was created by another window.
     // This is used when determining whether the WebContents is allowed to be
     // closed via window.close(). This may be true even with a null |opener|
     // (e.g., for blocked popups), or when the window is opened with "noopener".
-    bool opened_by_another_window;
+    bool opened_by_another_window = false;
 
     // The name of the top-level frame of the new window. It is non-empty
     // when creating a named window (e.g. <a target="foo"> or
@@ -169,20 +170,20 @@
     GURL initial_popup_url;
 
     // True if the contents should be initially hidden.
-    bool initially_hidden;
+    bool initially_hidden = false;
 
     // If non-null then this WebContents will be hosted by a BrowserPlugin.
-    raw_ptr<BrowserPluginGuestDelegate> guest_delegate;
+    raw_ptr<BrowserPluginGuestDelegate> guest_delegate = nullptr;
 
     // Used to specify the location context which display the new view should
     // belong. This can be nullptr if not needed.
-    gfx::NativeView context;
+    gfx::NativeView context = nullptr;
 
     // Used to specify that the new WebContents creation is driven by the
     // renderer process. In this case, the renderer-side objects, such as
     // RenderFrame, have already been created on the renderer side, and
     // WebContents construction should take this into account.
-    bool renderer_initiated_creation;
+    bool renderer_initiated_creation = false;
 
     // Used to specify how far WebContents::Create can initialize a renderer
     // process.
@@ -227,10 +228,11 @@
       // WebContents and/or 2) speculative RenderFrameHost used internally
       // during a navigation.
       kInitializeAndWarmupRendererProcess,
-    } desired_renderer_state;
+    } desired_renderer_state = kOkayToHaveRendererProcess;
 
     // Sandboxing flags set on the new WebContents.
-    network::mojom::WebSandboxFlags starting_sandbox_flags;
+    network::mojom::WebSandboxFlags starting_sandbox_flags =
+        network::mojom::WebSandboxFlags::kNone;
 
     // Value used to set the last time the WebContents was made active, this is
     // the value that'll be returned by GetLastActiveTime(). If this is left
@@ -242,7 +244,7 @@
     // first time it is shown. Some WebContents are never shown though.
     // Setting this to true will invoke the WebContents delayed initialization
     // that doesn't require visibility.
-    bool is_never_visible;
+    bool is_never_visible = false;
 
     // Code location responsible for creating the CreateParams.  This is used
     // mostly for debugging (e.g. to help attribute specific scenarios or
@@ -251,7 +253,7 @@
 
     // Enables contents to hold wake locks, for example, to keep the screen on
     // while playing video.
-    bool enable_wake_locks;
+    bool enable_wake_locks = true;
   };
 
   // Creates a new WebContents.
@@ -316,7 +318,7 @@
   CONTENT_EXPORT static void SetScreenOrientationDelegate(
       ScreenOrientationDelegate* delegate);
 
-  ~WebContents() override {}
+  ~WebContents() override = default;
 
   // Intrinsic tab state -------------------------------------------------------
 
@@ -1345,7 +1347,7 @@
  private:
   // This interface should only be implemented inside content.
   friend class WebContentsImpl;
-  WebContents() {}
+  WebContents() = default;
 };
 
 }  // namespace content
diff --git a/content/public/test/test_aggregation_service.h b/content/public/test/test_aggregation_service.h
index 7cbfad0..9ebd22e 100644
--- a/content/public/test/test_aggregation_service.h
+++ b/content/public/test/test_aggregation_service.h
@@ -37,8 +37,8 @@
 
   // This is 1-1 mapping of AggregationServicePayloadContents::Operation.
   enum class Operation {
-    kHierarchicalHistogram = 0,
-    kMaxValue = kHierarchicalHistogram,
+    kHistogram = 0,
+    kMaxValue = kHistogram,
   };
 
   // This is 1-1 mapping of AggregationServicePayloadContent::ProcessingType.
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index 29c461ea8..a973c8d2 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -693,6 +693,7 @@
       // already a base factory type set. Instead manually set the new base
       // factory type with SetBaseRendererType.
       factory_selector->SetBaseRendererType(RendererType::kMediaFoundation);
+      is_base_renderer_factory_set = true;
     }
   }
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/content/renderer/service_worker/navigation_preload_request.cc b/content/renderer/service_worker/navigation_preload_request.cc
index 92f9d4a..bab0bb2f 100644
--- a/content/renderer/service_worker/navigation_preload_request.cc
+++ b/content/renderer/service_worker/navigation_preload_request.cc
@@ -18,7 +18,7 @@
 NavigationPreloadRequest::NavigationPreloadRequest(
     ServiceWorkerContextClient* owner,
     int fetch_event_id,
-    const GURL& url,
+    const blink::WebURL& url,
     mojo::PendingReceiver<network::mojom::URLLoaderClient>
         preload_url_loader_client_receiver)
     : owner_(owner),
@@ -86,7 +86,7 @@
 void NavigationPreloadRequest::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
   if (status.error_code != net::OK) {
-    std::string message;
+    blink::WebString message;
     blink::WebServiceWorkerError::Mode error_mode =
         blink::WebServiceWorkerError::Mode::kNone;
     if (status.error_code == net::ERR_ABORTED) {
@@ -130,13 +130,13 @@
 }
 
 void NavigationPreloadRequest::ReportErrorToOwner(
-    const std::string& message,
+    const blink::WebString& message,
     blink::WebServiceWorkerError::Mode error_mode) {
   // This will delete |this|.
   owner_->OnNavigationPreloadError(
-      fetch_event_id_, std::make_unique<blink::WebServiceWorkerError>(
-                           blink::mojom::ServiceWorkerErrorType::kNetwork,
-                           blink::WebString::FromUTF8(message), error_mode));
+      fetch_event_id_,
+      std::make_unique<blink::WebServiceWorkerError>(
+          blink::mojom::ServiceWorkerErrorType::kNetwork, message, error_mode));
 }
 
 }  // namespace content
diff --git a/content/renderer/service_worker/navigation_preload_request.h b/content/renderer/service_worker/navigation_preload_request.h
index f6d19a3d..36a25a1 100644
--- a/content/renderer/service_worker/navigation_preload_request.h
+++ b/content/renderer/service_worker/navigation_preload_request.h
@@ -6,8 +6,6 @@
 #define CONTENT_RENDERER_SERVICE_WORKER_NAVIGATION_PRELOAD_REQUEST_H_
 
 #include <memory>
-#include <string>
-#include <vector>
 
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -16,8 +14,9 @@
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/dispatch_fetch_event_params.mojom.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_response.h"
-#include "url/gurl.h"
 
 namespace content {
 
@@ -34,7 +33,7 @@
   NavigationPreloadRequest(
       ServiceWorkerContextClient* owner,
       int fetch_event_id,
-      const GURL& url,
+      const blink::WebURL& url,
       mojo::PendingReceiver<network::mojom::URLLoaderClient>
           preload_url_loader_client_receiver);
   ~NavigationPreloadRequest() override;
@@ -57,13 +56,13 @@
 
  private:
   void MaybeReportResponseToOwner();
-  void ReportErrorToOwner(const std::string& message,
+  void ReportErrorToOwner(const blink::WebString& message,
                           blink::WebServiceWorkerError::Mode error_mode);
 
   ServiceWorkerContextClient* owner_ = nullptr;
 
   const int fetch_event_id_ = -1;
-  const GURL url_;
+  const blink::WebURL url_;
   mojo::Receiver<network::mojom::URLLoaderClient> receiver_;
 
   std::unique_ptr<blink::WebURLResponse> response_;
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 93539e61a..abf30d6c 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -481,8 +481,7 @@
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(context_);
   auto preload_request = std::make_unique<NavigationPreloadRequest>(
-      this, fetch_event_id, GURL(url),
-      std::move(preload_url_loader_client_receiver));
+      this, fetch_event_id, url, std::move(preload_url_loader_client_receiver));
   context_->preload_requests.AddWithID(std::move(preload_request),
                                        fetch_event_id);
 }
diff --git a/content/renderer/service_worker/service_worker_type_converters.cc b/content/renderer/service_worker/service_worker_type_converters.cc
index f24fb64..f667e38 100644
--- a/content/renderer/service_worker/service_worker_type_converters.cc
+++ b/content/renderer/service_worker/service_worker_type_converters.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 
 namespace mojo {
 
diff --git a/content/renderer/service_worker/service_worker_type_converters.h b/content/renderer/service_worker/service_worker_type_converters.h
index e216d6e..af9331c 100644
--- a/content/renderer/service_worker/service_worker_type_converters.h
+++ b/content/renderer/service_worker/service_worker_type_converters.h
@@ -8,7 +8,7 @@
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-forward.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_object_info.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_registration_object_info.h"
 #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h"
diff --git a/content/renderer/service_worker/web_service_worker_provider_impl.h b/content/renderer/service_worker/web_service_worker_provider_impl.h
index 92440056..7c33cfe6 100644
--- a/content/renderer/service_worker/web_service_worker_provider_impl.h
+++ b/content/renderer/service_worker/web_service_worker_provider_impl.h
@@ -12,7 +12,8 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom-forward.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-forward.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-forward.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider.h"
 
diff --git a/content/shell/browser/shell_platform_delegate_views.cc b/content/shell/browser/shell_platform_delegate_views.cc
index efc3073..16eb7db1 100644
--- a/content/shell/browser/shell_platform_delegate_views.cc
+++ b/content/shell/browser/shell_platform_delegate_views.cc
@@ -7,8 +7,10 @@
 
 #include <stddef.h>
 
+#include <algorithm>
 #include <memory>
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/cxx17_backports.h"
@@ -34,10 +36,13 @@
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/controls/webview/webview.h"
-#include "ui/views/layout/fill_layout.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/test/desktop_test_views_delegate.h"
 #include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -77,7 +82,8 @@
 namespace {
 
 // Maintain the UI controls and web view for content shell
-class ShellView : public views::View, public views::TextfieldController {
+class ShellView : public views::BoxLayoutView,
+                  public views::TextfieldController {
  public:
   METADATA_HEADER(ShellView);
 
@@ -94,20 +100,20 @@
   }
 
   void SetWebContents(WebContents* web_contents, const gfx::Size& size) {
-    contents_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
     // If there was a previous WebView in this Shell it should be removed and
     // deleted.
-    if (web_view_) {
-      contents_view_->RemoveChildView(web_view_);
-      delete web_view_;
-    }
-    auto web_view =
-        std::make_unique<views::WebView>(web_contents->GetBrowserContext());
-    web_view->SetWebContents(web_contents);
-    web_view->SetPreferredSize(size);
+    if (web_view_)
+      contents_view_->RemoveChildViewT(web_view_.get());
+
+    views::Builder<views::View>(contents_view_)
+        .AddChild(views::Builder<views::WebView>()
+                      .CopyAddressTo(&web_view_)
+                      .SetBrowserContext(web_contents->GetBrowserContext())
+                      .SetWebContents(web_contents)
+                      .SetPreferredSize(size))
+        .BuildChildren();
     web_contents->Focus();
-    web_view_ = contents_view_->AddChildView(std::move(web_view));
-    Layout();
+    web_view_->SizeToPreferredSize();
 
     // Resize the widget, keeping the same origin.
     gfx::Rect bounds = GetWidget()->GetWindowBoundsInScreen();
@@ -137,105 +143,98 @@
  private:
   // Initialize the UI control contained in shell window
   void InitShellWindow() {
-    SetBackground(
-        CreateThemedSolidBackground(this, ui::kColorWindowBackground));
+    auto toolbar_button_rule = [](const views::View* view,
+                                  const views::SizeBounds& size_bounds) {
+      gfx::Size preferred_size = view->GetPreferredSize();
+      if (size_bounds != views::SizeBounds() &&
+          size_bounds.width().is_bounded()) {
+        preferred_size.set_width(std::max(
+            std::min(size_bounds.width().value(), preferred_size.width()),
+            preferred_size.width() / 2));
+      }
+      return preferred_size;
+    };
 
-    auto contents_view = std::make_unique<views::View>();
-    auto toolbar_view = std::make_unique<views::View>();
+    auto builder =
+        views::Builder<views::BoxLayoutView>(this)
+            .SetBackground(
+                CreateThemedSolidBackground(this, ui::kColorWindowBackground))
+            .SetOrientation(views::BoxLayout::Orientation::kVertical);
 
-    views::GridLayout* layout =
-        SetLayoutManager(std::make_unique<views::GridLayout>());
-
-    using ColumnSize = views::GridLayout::ColumnSize;
-    views::ColumnSet* column_set = layout->AddColumnSet(0);
-    if (!Shell::ShouldHideToolbar())
-      column_set->AddPaddingColumn(0, 2);
-    column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
-                          ColumnSize::kUsePreferred, 0, 0);
-    if (!Shell::ShouldHideToolbar())
-      column_set->AddPaddingColumn(0, 2);
-
-    // Add toolbar buttons and URL text field
     if (!Shell::ShouldHideToolbar()) {
-      layout->AddPaddingRow(0, 2);
-      layout->StartRow(0, 0);
-      views::GridLayout* toolbar_layout =
-          toolbar_view->SetLayoutManager(std::make_unique<views::GridLayout>());
-
-      views::ColumnSet* toolbar_column_set = toolbar_layout->AddColumnSet(0);
-      // Back button
-      // Using Unretained (here and below) is safe since the View itself has the
-      // same lifetime as |shell_| (both are torn down implicitly during
-      // destruction).
-      auto back_button = std::make_unique<views::MdTextButton>(
-          base::BindRepeating(&Shell::GoBackOrForward,
-                              base::Unretained(shell_.get()), -1),
-          u"Back");
-      gfx::Size back_button_size = back_button->GetPreferredSize();
-      toolbar_column_set->AddColumn(
-          views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
-          ColumnSize::kFixed, back_button_size.width(),
-          back_button_size.width() / 2);
-      // Forward button
-      auto forward_button = std::make_unique<views::MdTextButton>(
-          base::BindRepeating(&Shell::GoBackOrForward,
-                              base::Unretained(shell_.get()), 1),
-          u"Forward");
-      gfx::Size forward_button_size = forward_button->GetPreferredSize();
-      toolbar_column_set->AddColumn(
-          views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
-          ColumnSize::kFixed, forward_button_size.width(),
-          forward_button_size.width() / 2);
-      // Refresh button
-      auto refresh_button = std::make_unique<views::MdTextButton>(
-          base::BindRepeating(&Shell::Reload, base::Unretained(shell_.get())),
-          u"Refresh");
-      gfx::Size refresh_button_size = refresh_button->GetPreferredSize();
-      toolbar_column_set->AddColumn(
-          views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
-          ColumnSize::kFixed, refresh_button_size.width(),
-          refresh_button_size.width() / 2);
-      // Stop button
-      auto stop_button = std::make_unique<views::MdTextButton>(
-          base::BindRepeating(&Shell::Stop, base::Unretained(shell_.get())),
-          u"Stop");
-      gfx::Size stop_button_size = stop_button->GetPreferredSize();
-      toolbar_column_set->AddColumn(
-          views::GridLayout::CENTER, views::GridLayout::CENTER, 0,
-          ColumnSize::kFixed, stop_button_size.width(),
-          stop_button_size.width() / 2);
-      toolbar_column_set->AddPaddingColumn(0, 2);
-      // URL entry
-      auto url_entry = std::make_unique<views::Textfield>();
-      url_entry->SetAccessibleName(u"Enter URL");
-      url_entry->set_controller(this);
-      url_entry->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_URL);
-      toolbar_column_set->AddColumn(views::GridLayout::FILL,
-                                    views::GridLayout::FILL, 1,
-                                    ColumnSize::kUsePreferred, 0, 0);
-      toolbar_column_set->AddPaddingColumn(0, 2);
-
-      // Fill up the first row
-      toolbar_layout->StartRow(0, 0);
-      back_button_ = toolbar_layout->AddView(std::move(back_button));
-      forward_button_ = toolbar_layout->AddView(std::move(forward_button));
-      refresh_button_ = toolbar_layout->AddView(std::move(refresh_button));
-      stop_button_ = toolbar_layout->AddView(std::move(stop_button));
-      url_entry_ = toolbar_layout->AddView(std::move(url_entry));
-
-      toolbar_view_ = layout->AddView(std::move(toolbar_view));
-
-      layout->AddPaddingRow(0, 5);
+      builder.AddChild(
+          views::Builder<views::FlexLayoutView>()
+              .CopyAddressTo(&toolbar_view_)
+              .SetOrientation(views::LayoutOrientation::kHorizontal)
+              // Top padding = 2, Bottom padding = 5
+              .SetProperty(views::kMarginsKey, gfx::Insets(2, 0, 5, 0))
+              .AddChildren(
+                  views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&back_button_)
+                      .SetText(u"Back")
+                      .SetCallback(base::BindRepeating(
+                          &Shell::GoBackOrForward,
+                          base::Unretained(shell_.get()), -1))
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   views::FlexSpecification(base::BindRepeating(
+                                       toolbar_button_rule))),
+                  views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&forward_button_)
+                      .SetText(u"Forward")
+                      .SetCallback(base::BindRepeating(
+                          &Shell::GoBackOrForward,
+                          base::Unretained(shell_.get()), 1))
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   views::FlexSpecification(base::BindRepeating(
+                                       toolbar_button_rule))),
+                  views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&refresh_button_)
+                      .SetText(u"Refresh")
+                      .SetCallback(base::BindRepeating(
+                          &Shell::Reload, base::Unretained(shell_.get())))
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   views::FlexSpecification(base::BindRepeating(
+                                       toolbar_button_rule))),
+                  views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&stop_button_)
+                      .SetText(u"Stop")
+                      .SetCallback(base::BindRepeating(
+                          &Shell::Stop, base::Unretained(shell_.get())))
+                      .SetProperty(views::kFlexBehaviorKey,
+                                   views::FlexSpecification(base::BindRepeating(
+                                       toolbar_button_rule))),
+                  views::Builder<views::Textfield>()
+                      .CopyAddressTo(&url_entry_)
+                      .SetAccessibleName(u"Enter URL")
+                      .SetController(this)
+                      .SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_URL)
+                      .SetProperty(
+                          views::kFlexBehaviorKey,
+                          views::FlexSpecification(
+                              views::MinimumFlexSizeRule::kScaleToMinimum,
+                              views::MaximumFlexSizeRule::kUnbounded))
+                      // Left padding  = 2, Right padding = 2
+                      .SetProperty(views::kMarginsKey,
+                                   gfx::Insets(0, 2, 0, 2))));
     }
 
-    // Add web contents view as the second row
-    {
-      layout->StartRow(1, 0);
-      contents_view_ = layout->AddView(std::move(contents_view));
+    builder.AddChild(views::Builder<views::View>()
+                         .CopyAddressTo(&contents_view_)
+                         .SetUseDefaultFillLayout(true)
+                         .CustomConfigure(base::BindOnce([](views::View* view) {
+                           if (!Shell::ShouldHideToolbar()) {
+                             view->SetProperty(views::kMarginsKey,
+                                               gfx::Insets(0, 2, 0, 2));
+                           }
+                         })));
+
+    if (!Shell::ShouldHideToolbar()) {
+      builder.AddChild(views::Builder<views::View>().SetProperty(
+          views::kMarginsKey, gfx::Insets(0, 0, 5, 0)));
     }
 
-    if (!Shell::ShouldHideToolbar())
-      layout->AddPaddingRow(0, 5);
+    std::move(builder).BuildChildren();
+    SetFlexForView(contents_view_, 1);
   }
   void InitAccelerators() {
     // This function must be called when part of the widget hierarchy.
@@ -299,7 +298,7 @@
   std::u16string title_;
 
   // Toolbar view contains forward/backward/reload button and URL entry
-  raw_ptr<View> toolbar_view_ = nullptr;
+  raw_ptr<views::View> toolbar_view_ = nullptr;
   raw_ptr<views::Button> back_button_ = nullptr;
   raw_ptr<views::Button> forward_button_ = nullptr;
   raw_ptr<views::Button> refresh_button_ = nullptr;
@@ -307,7 +306,7 @@
   raw_ptr<views::Textfield> url_entry_ = nullptr;
 
   // Contents view contains the web contents view
-  raw_ptr<View> contents_view_ = nullptr;
+  raw_ptr<views::View> contents_view_ = nullptr;
   raw_ptr<views::WebView> web_view_ = nullptr;
 };
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index a4970de3..b3e13759 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -84,6 +84,7 @@
 crbug.com/1256493 [ chromeos-board-kevin ] WebCodecs_copyTo_hw_decoder [ Failure ]
 crbug.com/1207682 [ android ] WebCodecs_* [ RetryOnFailure ]
 crbug.com/1275034 [ win nvidia ] WebCodecs_SVC_* [ Failure ]
+crbug.com/1277585 [ win amd ] WebCodecs_SVC_* [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/content/test/test_aggregation_service_impl.cc b/content/test/test_aggregation_service_impl.cc
index c41f8c9..48e865c 100644
--- a/content/test/test_aggregation_service_impl.cc
+++ b/content/test/test_aggregation_service_impl.cc
@@ -35,9 +35,8 @@
 AggregationServicePayloadContents::Operation ConvertToOperation(
     TestAggregationService::Operation operation) {
   switch (operation) {
-    case TestAggregationService::Operation::kHierarchicalHistogram:
-      return AggregationServicePayloadContents::Operation::
-          kHierarchicalHistogram;
+    case TestAggregationService::Operation::kHistogram:
+      return AggregationServicePayloadContents::Operation::kHistogram;
   }
 }
 
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn
index ee128320..fee01b9 100644
--- a/content/utility/BUILD.gn
+++ b/content/utility/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//chromeos/assistant/assistant.gni")
 import("//device/vr/buildflags/buildflags.gni")
+import("//media/gpu/args.gni")
 import("//media/media_options.gni")
 import("//printing/buildflags/buildflags.gni")
 
@@ -85,6 +86,9 @@
       "//chromeos/assistant:buildflags",
       "//chromeos/services/tts:sandbox_hook",
     ]
+    if (use_vaapi || use_v4l2_codec) {
+      deps += [ "//ash/components/arc/video_accelerator" ]
+    }
   }
 
   if (is_chromeos_ash && is_chrome_branded) {
diff --git a/content/utility/DEPS b/content/utility/DEPS
index 38c594a..81ecc04 100644
--- a/content/utility/DEPS
+++ b/content/utility/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/components/arc/video_accelerator",
   "+ash/services/ime",
   "+components/services/storage",
   "+content/child",
diff --git a/content/utility/services.cc b/content/utility/services.cc
index bac8051..297571dc 100644
--- a/content/utility/services.cc
+++ b/content/utility/services.cc
@@ -23,6 +23,7 @@
 #include "content/services/auction_worklet/auction_worklet_service_impl.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "device/vr/buildflags/buildflags.h"
+#include "media/gpu/buildflags.h"
 #include "media/media_buildflags.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/service_factory.h"
@@ -83,6 +84,12 @@
 #include "media/mojo/services/media_foundation_service_broker.h"  // nogncheck
 #endif  // defined(OS_WIN)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) && \
+    (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
+#include "ash/components/arc/video_accelerator/oop_arc_video_accelerator_factory.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) && (BUILDFLAG(USE_VAAPI) ||
+        // BUILDFLAG(USE_V4L2_CODEC))
+
 namespace content {
 
 namespace {
@@ -278,6 +285,16 @@
 }
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) && \
+    (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
+auto RunOOPArcVideoAcceleratorFactoryService(
+    mojo::PendingReceiver<arc::mojom::VideoAcceleratorFactory> receiver) {
+  return std::make_unique<arc::OOPArcVideoAcceleratorFactory>(
+      std::move(receiver));
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) && (BUILDFLAG(USE_VAAPI) ||
+        // BUILDFLAG(USE_V4L2_CODEC))
+
 }  // namespace
 
 void RegisterIOThreadServices(mojo::ServiceFactory& services) {
@@ -314,6 +331,12 @@
   services.Add(RunXrDeviceService);
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) && \
+    (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
+  services.Add(RunOOPArcVideoAcceleratorFactoryService);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) && (BUILDFLAG(USE_VAAPI) ||
+        // BUILDFLAG(USE_V4L2_CODEC))
+
   // Add new main-thread services above this line.
   GetContentClient()->utility()->RegisterMainThreadServices(services);
 }
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 67caf5f..6f8dc9e 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -544,7 +544,7 @@
     printer_->set_capture_text_only(false);
   printer_->reset();
 
-  accumulated_web_test_runtime_flags_changes_.Clear();
+  accumulated_web_test_runtime_flags_changes_.DictClear();
   web_test_runtime_flags_.Reset();
   main_window_render_view_hosts_.clear();
   main_window_render_process_hosts_.clear();
@@ -1222,9 +1222,9 @@
     main_window_->web_contents()->ExitFullscreen(/*will_cause_resize=*/false);
   devtools_bindings_.reset();
   devtools_protocol_test_bindings_.reset();
-  accumulated_web_test_runtime_flags_changes_.Clear();
+  accumulated_web_test_runtime_flags_changes_.DictClear();
   web_test_runtime_flags_.Reset();
-  work_queue_states_.Clear();
+  work_queue_states_.DictClear();
 
   ShellBrowserContext* browser_context =
       ShellContentBrowserClient::Get()->browser_context();
diff --git a/content/web_test/common/tracked_dictionary.cc b/content/web_test/common/tracked_dictionary.cc
index f0d283d..a5eb9bf 100644
--- a/content/web_test/common/tracked_dictionary.cc
+++ b/content/web_test/common/tracked_dictionary.cc
@@ -11,7 +11,7 @@
 TrackedDictionary::TrackedDictionary() {}
 
 void TrackedDictionary::ResetChangeTracking() {
-  changed_values_.Clear();
+  changed_values_.DictClear();
 }
 
 void TrackedDictionary::ApplyUntrackedChanges(
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 367ad32..e84c76c 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -367,10 +367,7 @@
 # chrome_extensions_browsertests.
 source_set("chrome_extensions_interactive_uitests") {
   testonly = true
-  sources = [
-    "browser/app_window/app_window_interactive_uitest.cc",
-    "browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc",
-  ]
+  sources = [ "browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc" ]
 
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
diff --git a/extensions/browser/api/lock_screen_data/data_item.cc b/extensions/browser/api/lock_screen_data/data_item.cc
index f96baaff..7293485 100644
--- a/extensions/browser/api/lock_screen_data/data_item.cc
+++ b/extensions/browser/api/lock_screen_data/data_item.cc
@@ -101,7 +101,7 @@
                         ValueStore* store) {
   ValueStore::ReadResult read = store->Get(kStoreKeyRegisteredItems);
 
-  values->Clear();
+  values->DictClear();
 
   if (!read.status().ok()) {
     *result = OperationResult::kFailed;
diff --git a/extensions/browser/api/storage/settings_quota_unittest.cc b/extensions/browser/api/storage/settings_quota_unittest.cc
index 9bb372a..d42b4e1e 100644
--- a/extensions/browser/api/storage/settings_quota_unittest.cc
+++ b/extensions/browser/api/storage/settings_quota_unittest.cc
@@ -244,7 +244,7 @@
   EXPECT_TRUE(SettingsEqual(settings));
 
   // Max out key count.
-  to_set.Clear();
+  to_set.DictClear();
   to_set.SetKey("b1", byte_value_1_.Clone());
   to_set.SetKey("b2", byte_value_1_.Clone());
   storage_->Set(DEFAULTS, to_set);
diff --git a/extensions/browser/event_listener_map.h b/extensions/browser/event_listener_map.h
index ae074de..e77d1cd 100644
--- a/extensions/browser/event_listener_map.h
+++ b/extensions/browser/event_listener_map.h
@@ -9,12 +9,13 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
 #include "extensions/common/event_filter.h"
 #include "extensions/common/extension_id.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace base {
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index d4106f58..7f7e304b 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1664,6 +1664,7 @@
   AUTOTESTPRIVATE_GETDISPLAYSMOOTHNESS = 1601,
   AUTOTESTPRIVATE_RESETHOLDINGSPACE = 1602,
   FILEMANAGERPRIVATEINTERNAL_GETDISALLOWEDTRANSFERS = 1603,
+  WMDESKSPRIVATE_GETDESKTEMPLATEJSON = 1604,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 3111988..6fa4fa66 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -245,8 +245,7 @@
 // static
 std::unique_ptr<HeadlessWebContentsImpl> HeadlessWebContentsImpl::Create(
     HeadlessWebContents::Builder* builder) {
-  content::WebContents::CreateParams create_params(builder->browser_context_,
-                                                   nullptr);
+  content::WebContents::CreateParams create_params(builder->browser_context_);
   auto headless_web_contents = base::WrapUnique(new HeadlessWebContentsImpl(
       content::WebContents::Create(create_params), builder->browser_context_));
 
diff --git "a/infra/config/generated/builders/ci/Mac10.15 Tests \050dbg\051/properties.textpb" "b/infra/config/generated/builders/ci/Mac10.15 Tests \050dbg\051/properties.textpb"
deleted file mode 100644
index ae30ff3..0000000
--- "a/infra/config/generated/builders/ci/Mac10.15 Tests \050dbg\051/properties.textpb"
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.mac",
-  "recipe": "chromium",
-  "sheriff_rotations": [
-    "chromium"
-  ]
-}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 4a88987..e9f31226 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -228,7 +228,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -315,7 +315,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -402,7 +402,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -489,7 +489,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -576,7 +576,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -663,7 +663,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -833,7 +833,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -920,7 +920,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1010,7 +1010,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1100,7 +1100,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1190,7 +1190,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1280,7 +1280,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1370,7 +1370,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1460,7 +1460,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1550,7 +1550,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1640,7 +1640,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1730,7 +1730,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1820,7 +1820,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -1910,7 +1910,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2000,7 +2000,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2090,7 +2090,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2177,7 +2177,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2267,7 +2267,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2357,7 +2357,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2447,7 +2447,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2537,7 +2537,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2627,7 +2627,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2717,7 +2717,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2807,7 +2807,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2897,7 +2897,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -2987,7 +2987,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3077,7 +3077,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3167,7 +3167,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3257,7 +3257,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3347,7 +3347,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3434,7 +3434,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3607,7 +3607,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3697,7 +3697,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3784,7 +3784,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3871,7 +3871,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -3958,7 +3958,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4045,7 +4045,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4132,7 +4132,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4219,7 +4219,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4304,7 +4304,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4389,7 +4389,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4476,7 +4476,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4563,7 +4563,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4650,7 +4650,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4737,7 +4737,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4824,7 +4824,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4911,7 +4911,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -4998,7 +4998,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5085,7 +5085,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5172,7 +5172,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5259,7 +5259,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5346,7 +5346,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5433,7 +5433,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5520,7 +5520,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5607,7 +5607,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5694,7 +5694,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5781,7 +5781,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5868,7 +5868,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -5955,7 +5955,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6042,7 +6042,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6551,7 +6551,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6641,7 +6641,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6731,7 +6731,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6821,7 +6821,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6911,7 +6911,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -6999,7 +6999,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7087,7 +7087,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7175,7 +7175,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7263,7 +7263,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7353,7 +7353,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7443,7 +7443,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7533,7 +7533,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7623,7 +7623,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7713,7 +7713,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7803,7 +7803,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7893,7 +7893,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -7983,7 +7983,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8071,7 +8071,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8159,7 +8159,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8249,7 +8249,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8339,7 +8339,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8429,7 +8429,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8519,7 +8519,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8609,7 +8609,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -8688,6 +8688,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -8764,6 +8768,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -8840,6 +8848,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -8916,6 +8928,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -8991,6 +9007,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9067,6 +9087,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9143,6 +9167,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9219,6 +9247,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9295,6 +9327,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9371,6 +9407,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9447,6 +9487,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9521,6 +9565,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9596,6 +9644,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9675,6 +9727,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 20
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -9762,7 +9818,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -9852,7 +9908,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -9942,7 +9998,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10032,7 +10088,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10122,7 +10178,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10212,7 +10268,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10302,7 +10358,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10389,7 +10445,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10479,7 +10535,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10566,7 +10622,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10656,7 +10712,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10746,7 +10802,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10836,7 +10892,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -10926,7 +10982,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11016,7 +11072,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11106,7 +11162,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11196,7 +11252,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11286,7 +11342,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11376,7 +11432,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11466,7 +11522,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11556,7 +11612,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11646,7 +11702,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11736,7 +11792,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11826,7 +11882,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -11916,7 +11972,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12006,7 +12062,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12096,7 +12152,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12186,7 +12242,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12276,7 +12332,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12366,7 +12422,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12456,7 +12512,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12546,7 +12602,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12636,7 +12692,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12726,7 +12782,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12816,7 +12872,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12903,7 +12959,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -12990,7 +13046,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13077,7 +13133,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13163,7 +13219,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13249,7 +13305,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13337,7 +13393,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13426,7 +13482,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13514,7 +13570,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13602,7 +13658,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13687,7 +13743,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13773,7 +13829,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13858,7 +13914,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -13948,7 +14004,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14038,7 +14094,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14128,7 +14184,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14218,7 +14274,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14308,7 +14364,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14398,7 +14454,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14488,7 +14544,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14578,7 +14634,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14668,7 +14724,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14758,7 +14814,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14848,7 +14904,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -14938,7 +14994,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15028,7 +15084,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15118,7 +15174,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15208,7 +15264,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15298,7 +15354,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15388,7 +15444,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15632,7 +15688,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15722,7 +15778,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15812,7 +15868,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15902,7 +15958,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -15992,98 +16048,8 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
-      }
-      experiments {
-        key: "luci.use_realms"
         value: 100
       }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
-      name: "Mac10.15 Tests (dbg)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/Mac10.15 Tests (dbg)/properties.textpb",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.mac",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium",'
-        '  "sheriff_rotations": ['
-        '    "chromium"'
-        '  ]'
-        '}'
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.chromium_tests.use_rdb_results"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 20
-      }
       experiments {
         key: "luci.use_realms"
         value: 100
@@ -16172,7 +16138,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16262,7 +16228,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16352,7 +16318,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16442,7 +16408,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16529,7 +16495,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16616,7 +16582,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16703,7 +16669,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16789,7 +16755,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16879,7 +16845,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -16969,7 +16935,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17059,7 +17025,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17146,7 +17112,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17233,7 +17199,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17320,7 +17286,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17410,7 +17376,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17500,7 +17466,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17590,7 +17556,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17680,7 +17646,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17770,7 +17736,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17860,7 +17826,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -17950,7 +17916,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18040,7 +18006,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18130,7 +18096,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18220,7 +18186,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18310,7 +18276,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18400,7 +18366,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18490,7 +18456,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18580,7 +18546,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18670,7 +18636,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18759,7 +18725,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -18927,7 +18893,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19017,7 +18983,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19107,7 +19073,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19197,7 +19163,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19286,7 +19252,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19375,7 +19341,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19464,7 +19430,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19632,7 +19598,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19721,7 +19687,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19811,7 +19777,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19901,7 +19867,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -19991,7 +19957,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20081,7 +20047,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20171,7 +20137,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20260,7 +20226,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20350,7 +20316,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20440,7 +20406,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20611,7 +20577,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20704,7 +20670,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20791,7 +20757,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20878,7 +20844,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -20965,7 +20931,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21055,7 +21021,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21145,7 +21111,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21235,7 +21201,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21324,7 +21290,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21410,7 +21376,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21500,7 +21466,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21586,7 +21552,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21672,7 +21638,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21761,7 +21727,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21850,7 +21816,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -21939,7 +21905,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22029,7 +21995,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22116,7 +22082,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22204,7 +22170,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22291,7 +22257,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22381,7 +22347,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22471,7 +22437,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22561,7 +22527,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22651,7 +22617,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22741,7 +22707,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22831,7 +22797,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -22921,7 +22887,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23011,7 +22977,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23101,7 +23067,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23191,7 +23157,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23281,7 +23247,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23371,7 +23337,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23460,7 +23426,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23549,7 +23515,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23635,7 +23601,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23725,7 +23691,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23815,7 +23781,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23905,7 +23871,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -23995,7 +23961,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24085,7 +24051,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24175,7 +24141,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24265,7 +24231,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24355,7 +24321,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24528,7 +24494,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24615,7 +24581,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24702,7 +24668,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24792,7 +24758,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -24879,7 +24845,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25335,7 +25301,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25425,7 +25391,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25515,7 +25481,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25605,7 +25571,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25696,7 +25662,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25865,7 +25831,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -25952,7 +25918,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26042,7 +26008,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26213,7 +26179,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26300,7 +26266,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26390,7 +26356,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26480,7 +26446,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26570,7 +26536,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26660,7 +26626,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26750,7 +26716,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26840,7 +26806,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -26930,7 +26896,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27020,7 +26986,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27186,7 +27152,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27276,7 +27242,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27366,7 +27332,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27456,7 +27422,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27546,7 +27512,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27636,7 +27602,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27726,7 +27692,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27816,7 +27782,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27906,7 +27872,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -27996,7 +27962,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28085,7 +28051,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28175,7 +28141,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28265,7 +28231,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28355,7 +28321,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28442,7 +28408,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28529,7 +28495,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28619,7 +28585,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28705,7 +28671,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -28972,7 +28938,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29059,7 +29025,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29149,7 +29115,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29239,7 +29205,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29329,7 +29295,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29416,7 +29382,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29503,7 +29469,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29590,7 +29556,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29680,7 +29646,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29770,7 +29736,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29857,7 +29823,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -29944,7 +29910,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30034,7 +30000,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30124,7 +30090,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30214,7 +30180,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30304,7 +30270,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30394,7 +30360,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30481,7 +30447,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30568,7 +30534,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30656,7 +30622,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30746,7 +30712,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30836,7 +30802,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -30926,7 +30892,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31093,7 +31059,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31180,7 +31146,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31267,7 +31233,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31354,7 +31320,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31441,7 +31407,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31528,7 +31494,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31615,7 +31581,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31704,7 +31670,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31794,7 +31760,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -31884,7 +31850,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32123,7 +32089,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32212,7 +32178,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32305,7 +32271,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32394,7 +32360,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32488,7 +32454,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32578,7 +32544,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32667,7 +32633,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32760,7 +32726,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32849,7 +32815,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -32942,7 +32908,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33031,7 +32997,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33120,7 +33086,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33209,7 +33175,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33298,7 +33264,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33387,7 +33353,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33476,7 +33442,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33566,7 +33532,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33656,7 +33622,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33743,7 +33709,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33830,7 +33796,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -33917,7 +33883,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34007,7 +33973,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34532,7 +34498,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34622,7 +34588,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34712,7 +34678,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34801,7 +34767,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34888,7 +34854,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -34984,7 +34950,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35071,7 +35037,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35161,7 +35127,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35251,7 +35217,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35338,7 +35304,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35425,7 +35391,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35512,7 +35478,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35599,7 +35565,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35686,7 +35652,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35773,7 +35739,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35863,7 +35829,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35953,7 +35919,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36040,7 +36006,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36130,7 +36096,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36217,7 +36183,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36307,7 +36273,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36394,7 +36360,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36481,7 +36447,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36571,7 +36537,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36658,7 +36624,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36748,7 +36714,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36835,7 +36801,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -36922,7 +36888,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37012,7 +36978,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37099,7 +37065,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37186,7 +37152,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37273,7 +37239,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37363,7 +37329,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37450,7 +37416,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37537,7 +37503,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37624,7 +37590,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37714,7 +37680,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37801,7 +37767,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37890,7 +37856,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -37977,7 +37943,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38063,7 +38029,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38153,7 +38119,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38401,7 +38367,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38491,7 +38457,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38581,7 +38547,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38668,7 +38634,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38758,7 +38724,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38846,7 +38812,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -38934,7 +38900,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39022,7 +38988,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39112,7 +39078,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39417,7 +39383,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39506,7 +39472,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39595,7 +39561,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39684,7 +39650,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39773,7 +39739,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39862,7 +39828,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -39947,7 +39913,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40032,7 +39998,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40121,7 +40087,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40208,7 +40174,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40295,7 +40261,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40382,7 +40348,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40468,7 +40434,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40554,7 +40520,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40642,7 +40608,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40729,7 +40695,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40814,7 +40780,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40899,7 +40865,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -40987,7 +40953,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41072,7 +41038,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41157,7 +41123,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41242,7 +41208,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41329,7 +41295,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41416,7 +41382,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41503,7 +41469,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41590,7 +41556,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41677,7 +41643,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41764,7 +41730,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41851,7 +41817,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -41938,7 +41904,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42025,7 +41991,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42112,7 +42078,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42202,7 +42168,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42289,7 +42255,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42376,7 +42342,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42850,7 +42816,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -42939,7 +42905,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43028,7 +42994,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43117,7 +43083,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43207,7 +43173,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43294,7 +43260,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43381,7 +43347,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43468,7 +43434,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43555,7 +43521,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43802,7 +43768,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43888,7 +43854,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -43975,7 +43941,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44065,7 +44031,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44471,7 +44437,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44561,7 +44527,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44648,7 +44614,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44735,7 +44701,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -44822,7 +44788,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45193,7 +45159,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45280,7 +45246,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45367,7 +45333,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45454,7 +45420,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45543,7 +45509,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45632,7 +45598,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45721,7 +45687,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45807,7 +45773,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45896,7 +45862,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -45983,7 +45949,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -46070,7 +46036,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -46157,7 +46123,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -46244,7 +46210,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -46473,7 +46439,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -46560,7 +46526,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47182,105 +47148,6 @@
         enable: true
       }
     }
-    builders {
-      name: "findit_variable"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "pool:luci.chromium.findit"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$build/goma": {'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org",'
-        '    "use_luci_auth": true'
-        '  },'
-        '  "$build/reclient": {'
-        '    "instance": "rbe-chromium-trusted",'
-        '    "jobs": 250,'
-        '    "metrics_project": "chromium-reclient-metrics"'
-        '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "recipe": "findit/chromium/compile"'
-        '}'
-      execution_timeout_secs: 28800
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "findit-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.chromium_tests.use_rdb_results"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
-      name: "linux_chromium_bot_db_exporter"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "os:Ubuntu-16.04|Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.findit"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "gs_bucket": "findit-for-me",'
-        '  "gs_object": "bot_db.json",'
-        '  "recipe": "findit/chromium/export_bot_db"'
-        '}'
-      execution_timeout_secs: 28800
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      build_numbers: YES
-      service_account: "findit-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.chromium_tests.use_rdb_results"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
   }
 }
 buckets {
@@ -47420,7 +47287,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47470,7 +47337,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47520,7 +47387,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47570,7 +47437,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47620,7 +47487,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47670,7 +47537,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47720,7 +47587,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47770,7 +47637,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47820,7 +47687,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47870,7 +47737,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47920,7 +47787,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -47970,7 +47837,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48020,7 +47887,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48070,7 +47937,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48120,7 +47987,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48170,7 +48037,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48220,7 +48087,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48270,7 +48137,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48320,7 +48187,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48375,7 +48242,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48426,7 +48293,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48477,7 +48344,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48527,7 +48394,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48577,7 +48444,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48626,7 +48493,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48676,7 +48543,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48726,7 +48593,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48776,7 +48643,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48826,7 +48693,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48876,7 +48743,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48926,7 +48793,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -48976,7 +48843,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49026,7 +48893,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49076,7 +48943,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49127,7 +48994,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49178,7 +49045,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49229,7 +49096,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49280,7 +49147,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49331,7 +49198,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49382,7 +49249,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49432,7 +49299,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49482,7 +49349,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49535,7 +49402,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49588,7 +49455,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49639,7 +49506,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49690,7 +49557,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49741,7 +49608,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49792,7 +49659,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49842,7 +49709,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49892,7 +49759,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -49966,7 +49833,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -50053,7 +49920,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -70604,6 +70471,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -79499,6 +79370,10 @@
         value: 100
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -82310,7 +82185,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82353,7 +82228,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82402,7 +82277,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82445,7 +82320,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82494,7 +82369,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82537,7 +82412,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82586,7 +82461,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82629,7 +82504,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82672,7 +82547,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82715,7 +82590,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82779,7 +82654,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82827,7 +82702,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82875,7 +82750,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82917,7 +82792,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -82959,7 +82834,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83007,7 +82882,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83055,7 +82930,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83097,7 +82972,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83145,7 +83020,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83193,7 +83068,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83235,7 +83110,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83283,7 +83158,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83331,7 +83206,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83373,7 +83248,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83415,7 +83290,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -83457,7 +83332,7 @@
       }
       experiments {
         key: "luci.recipes.use_python3"
-        value: 20
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 5be07677..2cd72c9d 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -231,11 +231,6 @@
     short_name: "11"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac10.15 Tests (dbg)"
-    category: "chromium.mac|debug"
-    short_name: "15"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/ios-device"
     category: "chromium.mac|ios|default"
     short_name: "dev"
@@ -979,11 +974,6 @@
     short_name: "11"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac10.15 Tests (dbg)"
-    category: "chromium.mac|debug"
-    short_name: "15"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/ios-simulator"
     category: "chromium.mac|ios|default"
     short_name: "sim"
@@ -9006,11 +8996,6 @@
     short_name: "11"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Mac10.15 Tests (dbg)"
-    category: "debug"
-    short_name: "15"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/ios-device"
     category: "ios|default"
     short_name: "dev"
@@ -12817,12 +12802,6 @@
   builders {
     name: "buildbucket/luci.chromium.findit/findit-rerun"
   }
-  builders {
-    name: "buildbucket/luci.chromium.findit/findit_variable"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.findit/linux_chromium_bot_db_exporter"
-  }
   builder_view_only: true
 }
 consoles {
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg
index e0aadcd..95dc509 100644
--- a/infra/config/generated/luci/luci-notify.cfg
+++ b/infra/config/generated/luci/luci-notify.cfg
@@ -1610,24 +1610,6 @@
   }
   builders {
     bucket: "ci"
-    name: "Mac10.15 Tests (dbg)"
-  }
-  tree_closers {
-    tree_status_host: "chromium-status.appspot.com"
-    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
-  }
-}
-notifiers {
-  notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
-    email {
-      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
-    }
-    template: "tree_closure_email_template"
-  }
-  builders {
-    bucket: "ci"
     name: "Mac11 Tests"
   }
   tree_closers {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 5a4e93b..c159b47 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -2477,20 +2477,6 @@
   }
 }
 job {
-  id: "Mac10.15 Tests (dbg)"
-  realm: "ci"
-  acls {
-    role: TRIGGERER
-    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.ci"
-    builder: "Mac10.15 Tests (dbg)"
-  }
-}
-job {
   id: "Mac11 Tests"
   realm: "ci"
   acls {
@@ -5898,17 +5884,6 @@
   }
 }
 job {
-  id: "linux_chromium_bot_db_exporter"
-  realm: "findit"
-  schedule: "0 0,6,12,18 * * *"
-  acl_sets: "findit"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.findit"
-    builder: "linux_chromium_bot_db_exporter"
-  }
-}
-job {
   id: "mac-angle-chromium-amd"
   realm: "ci"
   acls {
@@ -7402,16 +7377,6 @@
   }
 }
 acl_sets {
-  name: "findit"
-  acls {
-    role: OWNER
-    granted_to: "group:project-chromium-admins"
-  }
-  acls {
-    granted_to: "group:all"
-  }
-}
-acl_sets {
   name: "flakiness"
   acls {
     role: OWNER
diff --git a/infra/config/recipes.star b/infra/config/recipes.star
index bb4c2a50..d5124cf 100644
--- a/infra/config/recipes.star
+++ b/infra/config/recipes.star
@@ -131,9 +131,7 @@
 build_recipe(
     name = "recipe:chromium",
     bootstrappable = True,
-    experiments = {
-        "luci.recipes.use_python3": 20,
-    },
+    use_python3 = True,
 )
 
 build_recipe(
@@ -179,10 +177,16 @@
 
 build_recipe(
     name = "recipe:chromium_libfuzzer",
+    experiments = {
+        "luci.recipes.use_python3": 20,
+    },
 )
 
 build_recipe(
     name = "recipe:chromium_libfuzzer_trybot",
+    experiments = {
+        "luci.recipes.use_python3": 5,
+    },
 )
 
 build_recipe(
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 14d1ae3..d231fa67 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -6045,18 +6045,6 @@
 )
 
 ci.mac_thin_tester(
-    name = "Mac10.15 Tests (dbg)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug",
-        short_name = "15",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder (dbg)"],
-)
-
-ci.mac_thin_tester(
     name = "Mac11 Tests",
     branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
     console_view_entry = consoles.console_view_entry(
diff --git a/infra/config/subprojects/findit/consoles/findit.star b/infra/config/subprojects/findit/consoles/findit.star
deleted file mode 100644
index f243604e..0000000
--- a/infra/config/subprojects/findit/consoles/findit.star
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright 2020 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.
-
-luci.list_view(
-    name = "findit",
-    entries = [
-        "findit/findit-rerun",
-        "findit/findit_variable",
-        "findit/linux_chromium_bot_db_exporter",
-    ],
-)
diff --git a/infra/config/subprojects/findit/findit.star b/infra/config/subprojects/findit/findit.star
index 885532cd..8a622d5 100644
--- a/infra/config/subprojects/findit/findit.star
+++ b/infra/config/subprojects/findit/findit.star
@@ -2,7 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("//lib/builders.star", "builder", "defaults", "goma", "os")
+load("//lib/builders.star", "builder", "defaults", "goma")
+load("//lib/consoles.star", "consoles")
 load("//lib/ci.star", "rbe_instance", "rbe_jobs")
 load("//lib/swarming.star", swarming_lib = "swarming")
 
@@ -22,6 +23,10 @@
     ],
 )
 
+consoles.list_view(
+    name = "findit",
+)
+
 # FindIt builders use a separate pool with a dedicated set of permissions.
 swarming_lib.pool_realm(name = "pools/findit")
 
@@ -36,6 +41,7 @@
 defaults.bucket.set("findit")
 defaults.build_numbers.set(True)
 defaults.builderless.set(True)
+defaults.list_view.set("findit")
 defaults.ssd.set(True)
 defaults.execution_timeout.set(8 * time.hour)
 defaults.pool.set("luci.chromium.findit")
@@ -60,33 +66,3 @@
     reclient_instance = rbe_instance.DEFAULT,
     reclient_jobs = rbe_jobs.DEFAULT,
 )
-
-# Dimensionless trybot for findit.
-#
-# Findit will add appropriate dimensions and properties as needed based on
-# the waterfall builder being analyzed.
-#
-# TODO(robertocn): Remove _variable trybot builders from "try" bucket
-#   after they have been configured to use this generic builder, as well as
-#   the findit 'mixin'.
-builder(
-    name = "findit_variable",
-    # Findit app specifies these for each build it schedules. The reason why
-    # we specify them here is to pass validation of the buildbucket config.
-    # Also, to illustrate the typical use case of this bucket.
-    executable = "recipe:findit/chromium/compile",
-    goma_backend = goma.backend.RBE_PROD,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.DEFAULT,
-)
-
-builder(
-    name = "linux_chromium_bot_db_exporter",
-    executable = "recipe:findit/chromium/export_bot_db",
-    os = os.LINUX_XENIAL_OR_BIONIC_SWITCH_TO_DEFAULT,
-    properties = {
-        "gs_bucket": "findit-for-me",
-        "gs_object": "bot_db.json",
-    },
-    schedule = "0 0,6,12,18 * * *",
-)
diff --git a/infra/config/subprojects/findit/subproject.star b/infra/config/subprojects/findit/subproject.star
index 904f340d..f9e7fd7f 100644
--- a/infra/config/subprojects/findit/subproject.star
+++ b/infra/config/subprojects/findit/subproject.star
@@ -3,4 +3,3 @@
 # found in the LICENSE file.
 
 exec("./findit.star")
-exec("./consoles/findit.star")
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 5134aed..4701c72 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -220,7 +220,6 @@
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/metrics:metrics_internal",
-    "//ios/chrome/browser/net",
     "//ios/chrome/browser/ntp_snippets:ntp_snippets",
     "//ios/chrome/browser/policy:policy_util",
     "//ios/chrome/browser/search_engines",
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 042f2de..87ed0c3 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -32,6 +32,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/browsing_data/sessions_storage_util.h"
 #include "ios/chrome/browser/chrome_constants.h"
+#include "ios/chrome/browser/crash_report/crash_helper.h"
 #import "ios/chrome/browser/crash_report/crash_keys_helper.h"
 #include "ios/chrome/browser/crash_report/crash_keys_helper.h"
 #include "ios/chrome/browser/crash_report/crash_loop_detection_util.h"
@@ -305,9 +306,9 @@
   [[PreviousSessionInfo sharedInstance] resetMemoryWarningFlag];
   [[PreviousSessionInfo sharedInstance] stopRecordingMemoryFootprint];
 
-  // Turn off uploading of crash reports and metrics, in case the method of
-  // communication changes while in the background.
-  [MetricsMediator disableReporting];
+  // Turn off uploading of crash reports. This prevents failed uploads that will
+  // not be retried in Breakpad.
+  crash_helper::PauseBreakpadUploads();
 
   GetApplicationContext()->OnAppEnterBackground();
 }
@@ -375,6 +376,9 @@
         startRecordingMemoryFootprintWithInterval:
             base::Seconds(kMemoryFootprintRecordingTimeInterval)];
   }
+
+  // This will be a no-op if upload already started.
+  crash_helper::UploadCrashReports();
 }
 
 - (void)applicationWillTerminate:(UIApplication*)application {
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 862c430c..ca6bab47 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -27,6 +27,7 @@
 #include "ios/chrome/app/safe_mode_app_state_agent.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/crash_report/crash_helper.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
 #import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/main/test_browser.h"
@@ -55,6 +56,7 @@
 #import "ios/testing/scoped_block_swizzler.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "ios/web/public/thread/web_task_traits.h"
+#import "third_party/breakpad/breakpad/src/client/ios/BreakpadController.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "third_party/ocmock/gtest_support.h"
 
@@ -266,16 +268,17 @@
         safe_mode_swizzle_block_));
   }
 
-  void swizzleMetricsMediatorDisableReporting() {
-    metrics_mediator_called_ = NO;
+  void swizzleBreakpadUploadingDisabled() {
+    breakpad_disabled_called_ = NO;
 
-    metrics_mediator_swizzle_block_ = ^{
-      metrics_mediator_called_ = YES;
+    breakpad_swizzle_block_ = ^{
+      breakpad_disabled_called_ = YES;
     };
 
-    metrics_mediator_swizzler_.reset(new ScopedBlockSwizzler(
-        [MetricsMediator class], @selector(disableReporting),
-        metrics_mediator_swizzle_block_));
+    breakpad_swizzler_.reset(
+        new ScopedBlockSwizzler([BreakpadController class],
+                                NSSelectorFromString(@"setUploadingEnabled:"),
+                                breakpad_swizzle_block_));
   }
 
   void swizzleHandleStartupParameters(
@@ -431,8 +434,7 @@
   }
   ChromeBrowserState* getBrowserState() { return browser_state_.get(); }
 
-  BOOL metricsMediatorHasBeenCalled() { return metrics_mediator_called_; }
-
+  BOOL breakpadUploadingHasBeenDisabled() { return breakpad_disabled_called_; }
 
  private:
   web::WebTaskEnvironment task_environment_;
@@ -452,12 +454,12 @@
   ScenesBlock connected_scenes_swizzle_block_;
   DecisionBlock safe_mode_swizzle_block_;
   HandleStartupParam handle_startup_swizzle_block_;
-  ProceduralBlock metrics_mediator_swizzle_block_;
+  ProceduralBlock breakpad_swizzle_block_;
   std::unique_ptr<ScopedBlockSwizzler> safe_mode_swizzler_;
   std::unique_ptr<ScopedBlockSwizzler> connected_scenes_swizzler_;
   std::unique_ptr<ScopedBlockSwizzler> handle_startup_swizzler_;
-  std::unique_ptr<ScopedBlockSwizzler> metrics_mediator_swizzler_;
-  __block BOOL metrics_mediator_called_;
+  std::unique_ptr<ScopedBlockSwizzler> breakpad_swizzler_;
+  __block BOOL breakpad_disabled_called_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
 };
 
@@ -725,7 +727,6 @@
 
   // Simulate background before going to foreground.
   [[getStartupInformationMock() expect] expireFirstUserActionRecorder];
-  swizzleMetricsMediatorDisableReporting();
   [getAppStateWithMock() applicationDidEnterBackground:application
                                           memoryHelper:memoryHelper];
 
@@ -836,6 +837,7 @@
   id application = [OCMockObject niceMockForClass:[UIApplication class]];
   id memoryHelper = [OCMockObject mockForClass:[MemoryWarningHelper class]];
   StubBrowserInterfaceProvider* interfaceProvider = getInterfaceProvider();
+  crash_helper::SetEnabled(true);
 
   std::unique_ptr<Browser> browser = std::make_unique<TestBrowser>();
   id startupInformation = getStartupInformationMock();
@@ -850,7 +852,7 @@
   interfaceProvider.incognitoInterface.browser = browser.get();
   [[[browserLauncher stub] andReturn:interfaceProvider] interfaceProvider];
 
-  swizzleMetricsMediatorDisableReporting();
+  swizzleBreakpadUploadingDisabled();
 
   // Simulate launching the app before going to background. This is to start
   // initialization process.
@@ -866,7 +868,8 @@
 
   // Tests.
   EXPECT_OCMOCK_VERIFY(startupInformation);
-  EXPECT_TRUE(metricsMediatorHasBeenCalled());
+  EXPECT_TRUE(breakpadUploadingHasBeenDisabled());
+  crash_helper::SetEnabled(false);
 }
 
 // Tests that -applicationDidEnterBackground do nothing if the application has
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.h b/ios/chrome/app/application_delegate/metrics_mediator.h
index c02f0b82..ce56e664f 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.h
+++ b/ios/chrome/app/application_delegate/metrics_mediator.h
@@ -37,11 +37,8 @@
 // Returns YES if the metrics pref is enabled.  Does not take into account the
 // wifi-only option or wwan state.
 - (BOOL)areMetricsEnabled;
-// Return YES if uploading is allowed, based on user preferences.
-- (BOOL)isUploadingEnabled;
 // Starts or stops the metrics service and crash report recording and/or
-// uploading, based on the current user preferences. Makes sure helper
-// mechanisms and the wwan state observer are set up if necessary. Must be
+// uploading, based on the current user preferences. Must be
 // called both on initialization and after user triggered preference change.
 // |isUserTriggered| is used to distinguish between those cases.
 - (void)updateMetricsStateBasedOnPrefsUserTriggered:(BOOL)isUserTriggered;
@@ -56,8 +53,6 @@
 // Logs in UserDefaults the current date with kAppEnteredBackgroundDateKey as
 // key.
 + (void)logDateInUserDefaults;
-// Disables reporting in breakpad and metrics service.
-+ (void)disableReporting;
 // Logs that the application is in background and the number of memory warnings
 // for this session.
 + (void)applicationDidEnterBackground:(NSInteger)memoryWarningCount;
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 1eb0b83..28e0e0db 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -28,7 +28,6 @@
 #include "ios/chrome/browser/crash_report/crash_helper.h"
 #include "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/metrics/first_user_action_recorder.h"
-#import "ios/chrome/browser/net/connection_type_observer_bridge.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
@@ -249,35 +248,14 @@
 
 using metrics_mediator::kAppEnteredBackgroundDateKey;
 
-@interface MetricsMediator ()<CRConnectionTypeObserverBridge> {
-  // Whether or not the crash reports present at startup have been processed to
-  // determine if the last app lifetime ended in an OOM crash.
-  BOOL _hasProcessedCrashReportsPresentAtStartup;
-
-  // Observer for the connection type.  Contains a valid object only if the
-  // metrics setting is set to wifi-only.
-  std::unique_ptr<ConnectionTypeObserverBridge> _connectionTypeObserverBridge;
-}
-
-// Starts or stops metrics recording and/or uploading.
-- (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading;
+@interface MetricsMediator ()
+// Starts or stops metrics recording.
+- (void)setMetricsEnabled:(BOOL)enabled;
 // Sets variables needed by the app_group application to collect UMA data.
 // Process the pending logs produced by extensions.
 // Called on start (cold and warm) and UMA settings change to update the
 // collecting settings in extensions.
 - (void)setAppGroupMetricsEnabled:(BOOL)enabled;
-// Processes crash reports present at startup.
-- (void)processCrashReportsPresentAtStartup;
-// Starts or stops crash recording and/or uploading.
-- (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading;
-// Starts or stops watching for wwan events.
-- (void)setWatchWWANEnabled:(BOOL)enabled;
-// Enable/disable transmission of accumulated logs and crash reports (dumps).
-- (void)setReporting:(BOOL)enableReporting;
-// Enable/Disable uploading crash reports.
-- (void)setBreakpadUploadingEnabled:(BOOL)enableUploading;
-// Returns YES if the metrics are enabled and the reporting is wifi-only.
-- (BOOL)isMetricsReportingEnabledWifiOnly;
 // Update metrics prefs on a permission (opt-in/out) change. When opting out,
 // this clears various client ids. When opting in, this resets saving crash
 // prefs, so as not to trigger upload of various stale data.
@@ -442,12 +420,10 @@
 
 - (void)updateMetricsStateBasedOnPrefsUserTriggered:(BOOL)isUserTriggered {
   BOOL optIn = [self areMetricsEnabled];
-  BOOL allowUploading = [self isUploadingEnabled];
   if (isUserTriggered)
     [self updateMetricsPrefsOnPermissionChange:optIn];
-  [self setMetricsEnabled:optIn withUploading:allowUploading];
-  [self setBreakpadEnabled:optIn withUploading:allowUploading];
-  [self setWatchWWANEnabled:optIn];
+  [self setMetricsEnabled:optIn];
+  crash_helper::SetEnabled(optIn);
   [self setAppGroupMetricsEnabled:optIn];
   [[MetricKitSubscriber sharedInstance] setEnabled:optIn];
 }
@@ -466,13 +442,9 @@
   return optIn;
 }
 
-- (BOOL)isUploadingEnabled {
-  return [self areMetricsEnabled];
-}
-
 #pragma mark - Internal methods.
 
-- (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading {
+- (void)setMetricsEnabled:(BOOL)enabled {
   metrics::MetricsService* metrics =
       GetApplicationContext()->GetMetricsService();
   DCHECK(metrics);
@@ -482,10 +454,7 @@
     if (!metrics->recording_active())
       metrics->Start();
 
-    if (allowUploading)
-      metrics->EnableReporting();
-    else
-      metrics->DisableReporting();
+    metrics->EnableReporting();
   } else {
     if (metrics->recording_active())
       metrics->Stop();
@@ -512,43 +481,6 @@
   metrics_mediator::RecordWidgetUsage(kHistogramsFromExtension);
 }
 
-- (void)processCrashReportsPresentAtStartup {
-  _hasProcessedCrashReportsPresentAtStartup = YES;
-}
-
-- (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading {
-  crash_helper::SetUserEnabledUploading(enabled);
-  if (enabled) {
-    crash_helper::SetEnabled(true);
-
-    // Do some processing of the crash reports present at startup. Note that
-    // this processing must be done before uploading is enabled because once
-    // uploading starts the number of crash reports present will begin to
-    // decrease as they are uploaded. The ordering is ensured here because both
-    // the crash report processing and the upload enabling are handled by
-    // posting blocks to a single |dispath_queue_t| in BreakpadController.
-    if (!_hasProcessedCrashReportsPresentAtStartup && allowUploading) {
-      [self processCrashReportsPresentAtStartup];
-    }
-    [self setBreakpadUploadingEnabled:(![[PreviousSessionInfo sharedInstance]
-                                           isFirstSessionAfterUpgrade] &&
-                                       allowUploading)];
-  } else {
-    crash_helper::SetEnabled(false);
-  }
-}
-
-- (void)setWatchWWANEnabled:(BOOL)enabled {
-  if (!enabled) {
-    _connectionTypeObserverBridge.reset();
-    return;
-  }
-
-  if (!_connectionTypeObserverBridge) {
-    _connectionTypeObserverBridge.reset(new ConnectionTypeObserverBridge(self));
-  }
-}
-
 - (void)updateMetricsPrefsOnPermissionChange:(BOOL)enabled {
   // TODO(crbug.com/635669): Consolidate with metrics_reporting_state.cc
   // function.
@@ -575,14 +507,6 @@
   }
 }
 
-+ (void)disableReporting {
-  crash_helper::SetUploadingEnabled(false);
-  metrics::MetricsService* metrics =
-      GetApplicationContext()->GetMetricsService();
-  DCHECK(metrics);
-  metrics->DisableReporting();
-}
-
 + (void)applicationDidEnterBackground:(NSInteger)memoryWarningCount {
   base::RecordAction(base::UserMetricsAction("MobileEnteredBackground"));
   base::UmaHistogramCounts100("MemoryWarning.OccurrencesPerSession",
@@ -600,30 +524,6 @@
   }
 }
 
-#pragma mark - CRConnectionTypeObserverBridge implementation
-
-- (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type {
-  BOOL wwanEnabled = net::NetworkChangeNotifier::IsConnectionCellular(type);
-  // Currently the MainController only cares about WWAN state for the metrics
-  // reporting preference.  If it's disabled, or the wifi-only preference is
-  // not set, we don't care.  In fact, we should not even be getting this call.
-  DCHECK([self isMetricsReportingEnabledWifiOnly]);
-  // |wwanEnabled| is true if a cellular connection such as EDGE or GPRS is
-  // used. Otherwise, either there is no connection available, or another link
-  // (such as WiFi) is used.
-  if (wwanEnabled) {
-    // If WWAN mode is on, wifi-only prefs should be disabled.
-    // For the crash reporter, we still want to record the crashes.
-    [self setBreakpadUploadingEnabled:NO];
-    [self setReporting:NO];
-  } else if ([self areMetricsEnabled]) {
-    // Double-check that the metrics reporting preference is enabled.
-    if (![[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade])
-      [self setBreakpadUploadingEnabled:YES];
-    [self setReporting:YES];
-  }
-}
-
 #pragma mark - interfaces methods
 
 + (void)recordNumTabAtStartup:(int)numTabs {
@@ -646,21 +546,4 @@
   base::UmaHistogramCounts100("Tabs.LiveNTPCountAtResume", numTabs);
 }
 
-- (void)setBreakpadUploadingEnabled:(BOOL)enableUploading {
-  crash_helper::SetUploadingEnabled(enableUploading);
-}
-
-- (void)setReporting:(BOOL)enableReporting {
-  if (enableReporting) {
-    GetApplicationContext()->GetMetricsService()->EnableReporting();
-  } else {
-    GetApplicationContext()->GetMetricsService()->DisableReporting();
-  }
-}
-
-- (BOOL)isMetricsReportingEnabledWifiOnly {
-  return GetApplicationContext()->GetLocalState()->GetBoolean(
-      metrics::prefs::kMetricsReportingEnabled);
-}
-
 @end
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_testing.h b/ios/chrome/app/application_delegate/metrics_mediator_testing.h
index 6e3df526..d61fbfd 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_testing.h
+++ b/ios/chrome/app/application_delegate/metrics_mediator_testing.h
@@ -8,11 +8,6 @@
 #include "net/base/network_change_notifier.h"
 
 @interface MetricsMediator (TestingAddition)
-- (void)processCrashReportsPresentAtStartup;
-- (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type;
-- (void)setBreakpadUploadingEnabled:(BOOL)enableUploading;
-- (void)setReporting:(BOOL)enableReporting;
-- (BOOL)isMetricsReportingEnabledWifiOnly;
 + (void)recordNumTabAtStartup:(int)numTabs;
 + (void)recordNumTabAtResume:(int)numTabs;
 + (void)recordNumNTPTabAtStartup:(int)numTabs;
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
index 9a8ab6ee..eae2a75 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
@@ -34,101 +34,29 @@
 #error "This file requires ARC support."
 #endif
 
-#pragma mark - connectionTypeChanged tests.
-
 // Mock class for testing MetricsMediator.
 @interface MetricsMediatorMock : MetricsMediator
 @property(nonatomic) NSInteger reportingValue;
-@property(nonatomic) NSInteger breakpadUpload;
 - (void)reset;
-- (void)setReporting:(BOOL)enableReporting;
 @end
 
 @implementation MetricsMediatorMock
 @synthesize reportingValue = _reportingValue;
-@synthesize breakpadUpload = _breakpadUpload;
 
 - (void)reset {
   _reportingValue = -1;
-  _breakpadUpload = -1;
-}
-- (void)setBreakpadUploadingEnabled:(BOOL)enableUploading {
-  _breakpadUpload = enableUploading ? 1 : 0;
 }
 - (void)setReporting:(BOOL)enableReporting {
   _reportingValue = enableReporting ? 1 : 0;
 }
-- (BOOL)isMetricsReportingEnabledWifiOnly {
-  return YES;
-}
 - (BOOL)areMetricsEnabled {
   return YES;
 }
 
 @end
 
-// Gives the differents net::NetworkChangeNotifier::ConnectionType based on
-// scenario number.
-net::NetworkChangeNotifier::ConnectionType getConnectionType(int number) {
-  switch (number) {
-    case 0:
-      return net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
-    case 1:
-      return net::NetworkChangeNotifier::CONNECTION_ETHERNET;
-    case 2:
-      return net::NetworkChangeNotifier::CONNECTION_WIFI;
-    case 3:
-      return net::NetworkChangeNotifier::CONNECTION_2G;
-    case 4:
-      return net::NetworkChangeNotifier::CONNECTION_3G;
-    case 5:
-      return net::NetworkChangeNotifier::CONNECTION_4G;
-    case 6:
-      return net::NetworkChangeNotifier::CONNECTION_NONE;
-    case 7:
-      return net::NetworkChangeNotifier::CONNECTION_BLUETOOTH;
-    case 8:
-      return net::NetworkChangeNotifier::CONNECTION_5G;
-    default:
-      return net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
-  }
-}
-
-// Gives the differents expected value based on scenario number.
-int getExpectedValue(int number) {
-  // Cellular network types are expected to return 0.
-  switch (getConnectionType(number)) {
-    case net::NetworkChangeNotifier::CONNECTION_2G:
-    case net::NetworkChangeNotifier::CONNECTION_3G:
-    case net::NetworkChangeNotifier::CONNECTION_4G:
-    case net::NetworkChangeNotifier::CONNECTION_5G:
-      return 0;
-    default:
-      return 1;
-  }
-}
-
 using MetricsMediatorTest = PlatformTest;
 
-// Verifies that connectionTypeChanged correctly enables or disables the
-// uploading in the breakpad and in the metrics service.
-TEST_F(MetricsMediatorTest, connectionTypeChanged) {
-  [[PreviousSessionInfo sharedInstance] setIsFirstSessionAfterUpgrade:NO];
-  MetricsMediatorMock* mock_metrics_helper = [[MetricsMediatorMock alloc] init];
-
-  // Checks all different scenarios.
-  for (int i = 0; i < 9; ++i) {
-    [mock_metrics_helper reset];
-    [mock_metrics_helper connectionTypeChanged:getConnectionType(i)];
-    EXPECT_EQ(getExpectedValue(i), [mock_metrics_helper reportingValue]);
-    EXPECT_EQ(getExpectedValue(i), [mock_metrics_helper breakpadUpload]);
-  }
-
-  // Checks that no new ConnectionType has been added.
-  EXPECT_EQ(net::NetworkChangeNotifier::CONNECTION_5G,
-            net::NetworkChangeNotifier::CONNECTION_LAST);
-}
-
 // Tests that histograms logged in a widget are correctly re-emitted by Chrome.
 TEST_F(MetricsMediatorTest, WidgetHistogramMetricsRecorded) {
   using app_group::HistogramCountKey;
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 6b59050f..9a7b13f37 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -168,8 +168,8 @@
 // Constants for deferred sending of queued feedback.
 NSString* const kSendQueuedFeedback = @"SendQueuedFeedback";
 
-// Constants for deferring the deletion of pre-upgrade crash reports.
-NSString* const kCleanupCrashReports = @"CleanupCrashReports";
+// Constants for deferring the upload of crash reports.
+NSString* const kUploadCrashReports = @"UploadCrashReports";
 
 // Constants for deferring the cleanup of snapshots on disk.
 NSString* const kCleanupSnapshots = @"CleanupSnapshots";
@@ -331,8 +331,8 @@
 - (void)startFreeMemoryMonitoring;
 // Asynchronously schedules the reset of the failed startup attempt counter.
 - (void)scheduleStartupAttemptReset;
-// Asynchronously schedules the cleanup of crash reports.
-- (void)scheduleCrashReportCleanup;
+// Asynchronously schedules the upload of crash reports.
+- (void)scheduleCrashReportUpload;
 // Asynchronously schedules the cleanup of discarded session files on disk.
 - (void)scheduleDiscardedSessionsCleanup;
 // Asynchronously schedules the cleanup of snapshots on disk.
@@ -870,12 +870,11 @@
                   }];
 }
 
-- (void)scheduleCrashReportCleanup {
+- (void)scheduleCrashReportUpload {
   [[DeferredInitializationRunner sharedInstance]
-      enqueueBlockNamed:kCleanupCrashReports
+      enqueueBlockNamed:kUploadCrashReports
                   block:^{
-                    bool afterUpgrade = [self isFirstLaunchAfterUpgrade];
-                    crash_helper::CleanupCrashReports(afterUpgrade);
+                    crash_helper::UploadCrashReports();
                   }];
 }
 
@@ -907,7 +906,7 @@
 }
 
 - (void)scheduleStartupCleanupTasks {
-  [self scheduleCrashReportCleanup];
+  [self scheduleCrashReportUpload];
 
   // ClearSessionCookies() is not synchronous.
   if (cookie_util::ShouldClearSessionCookies()) {
@@ -1148,7 +1147,7 @@
 
 - (void)crashIfRequested {
   if (experimental_flags::IsStartupCrashEnabled()) {
-    // Flush out the value cached for breakpad::SetUploadingEnabled().
+    // Flush out the value cached for crash_helper::SetEnabled().
     [[NSUserDefaults standardUserDefaults] synchronize];
 
     int* x = NULL;
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
index 49881da..53e8ab0 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
+++ b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
@@ -87,6 +87,5 @@
     "application_breadcrumbs_logger_unittest.mm",
     "breadcrumb_manager_browser_agent_unittest.mm",
     "breadcrumb_manager_tab_helper_unittest.mm",
-    "breadcrumb_persistent_storage_manager_unittest.mm",
   ]
 }
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm
deleted file mode 100644
index 171152d5..0000000
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright 2020 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 "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/files/file.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#import "base/test/ios/wait_util.h"
-#include "components/breadcrumbs/core/breadcrumb_manager_keyed_service.h"
-#include "components/breadcrumbs/core/breadcrumb_persistent_storage_util.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
-#include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
-#include "ios/web/public/test/web_task_environment.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using base::test::ios::kWaitForFileOperationTimeout;
-using base::test::ios::WaitUntilConditionOrTimeout;
-
-namespace breadcrumbs {
-
-namespace {
-
-// Estimate number of events too large to fit in the persisted file. 6 is based
-// on the event string format which is slightly smaller than each event.
-constexpr unsigned long kEventCountTooManyForPersisting =
-    kPersistedFilesizeInBytes / 6.0;
-
-// Creates a new BreadcrumbManagerKeyedService for |browser_state|.
-std::unique_ptr<KeyedService> BuildBreadcrumbManagerKeyedService(
-    web::BrowserState* browser_state) {
-  return std::make_unique<BreadcrumbManagerKeyedService>(
-      browser_state->IsOffTheRecord());
-}
-
-// Validates that the events in |persisted_events| are contiguous and that the
-// |last_logged_event| matches the last persisted event.
-bool ValidatePersistedEvents(std::string last_logged_event,
-                             std::vector<std::string> persisted_events) {
-  if (persisted_events.empty()) {
-    return false;
-  }
-
-  std::string last_event = persisted_events.back();
-  if (last_event.find(last_logged_event) == std::string::npos) {
-    return false;
-  }
-
-  std::string oldest_event = persisted_events.front();
-  int first_event_index;
-  if (!base::StringToInt(
-          oldest_event.substr(oldest_event.find_last_of(' ') + 1),
-          &first_event_index)) {
-    // first_event_index could not be parsed.
-    return false;
-  }
-
-  std::string newest_event = persisted_events.back();
-  int last_event_index;
-  if (!base::StringToInt(
-          newest_event.substr(newest_event.find_last_of(' ') + 1),
-          &last_event_index)) {
-    // last_event_index could not be parsed.
-    return false;
-  }
-
-  // Validate no events are missing from within the persisted range.
-  return last_event_index - first_event_index + 1 ==
-         static_cast<int>(persisted_events.size());
-}
-
-}  // namespace
-
-class BreadcrumbPersistentStorageManagerTest : public PlatformTest {
- protected:
-  BreadcrumbPersistentStorageManagerTest()
-      : scoped_browser_state_manager_(
-            std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
-    EXPECT_TRUE(scoped_temp_directory_.CreateUniqueTempDir());
-    base::FilePath directory_name = scoped_temp_directory_.GetPath();
-
-    TestChromeBrowserState::Builder test_cbs_builder;
-    test_cbs_builder.SetPath(directory_name);
-    test_cbs_builder.AddTestingFactory(
-        BreadcrumbManagerKeyedServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildBreadcrumbManagerKeyedService));
-    chrome_browser_state_ = test_cbs_builder.Build();
-
-    breadcrumb_manager_service_ = static_cast<BreadcrumbManagerKeyedService*>(
-        BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
-            chrome_browser_state_.get()));
-    persistent_storage_ = std::make_unique<BreadcrumbPersistentStorageManager>(
-        directory_name,
-        breadcrumb_persistent_storage_util::
-            GetOldBreadcrumbPersistentStorageFilePath(directory_name),
-        breadcrumb_persistent_storage_util::
-            GetOldBreadcrumbPersistentStorageTempFilePath(directory_name));
-    breadcrumb_manager_service_->StartPersisting(persistent_storage_.get());
-  }
-
-  ~BreadcrumbPersistentStorageManagerTest() override {
-    breadcrumb_manager_service_->StopPersisting();
-  }
-
-  web::WebTaskEnvironment task_env_{
-      web::WebTaskEnvironment::Options::DEFAULT,
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
-  base::ScopedTempDir scoped_temp_directory_;
-  BreadcrumbManagerKeyedService* breadcrumb_manager_service_;
-  std::unique_ptr<BreadcrumbPersistentStorageManager> persistent_storage_;
-};
-
-// Ensures that logged events are persisted.
-TEST_F(BreadcrumbPersistentStorageManagerTest, PersistEvents) {
-  breadcrumb_manager_service_->AddEvent("event");
-
-  // Advance clock to trigger writing final events.
-  task_env_.FastForwardBy(base::Minutes(1));
-
-  __block bool events_received = false;
-  persistent_storage_->GetStoredEvents(
-      base::BindOnce(^(std::vector<std::string> events) {
-        ASSERT_EQ(1ul, events.size());
-        EXPECT_NE(std::string::npos, events.front().find("event"));
-        events_received = true;
-      }));
-
-  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
-    base::RunLoop().RunUntilIdle();
-    return events_received;
-  }));
-}
-
-// Ensures that persisted events do not grow too large for a single large event
-// bucket when events are logged very quickly one after the other.
-TEST_F(BreadcrumbPersistentStorageManagerTest, PersistLargeBucket) {
-  std::string event;
-  unsigned long event_count = 0;
-  while (event_count < kEventCountTooManyForPersisting) {
-    event = base::StringPrintf("event %lu", event_count);
-    breadcrumb_manager_service_->AddEvent(event);
-    task_env_.FastForwardBy(base::Milliseconds(1));
-
-    event_count++;
-  }
-
-  // Advance clock to trigger writing final events.
-  task_env_.FastForwardBy(base::Minutes(1));
-
-  __block bool events_received = false;
-  persistent_storage_->GetStoredEvents(
-      base::BindOnce(^(std::vector<std::string> events) {
-        EXPECT_LT(events.size(), event_count);
-
-        EXPECT_TRUE(ValidatePersistedEvents(event, events));
-        events_received = true;
-      }));
-
-  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
-    base::RunLoop().RunUntilIdle();
-    return events_received;
-  }));
-}
-
-// Ensures that persisted events do not grow too large for events logged a few
-// seconds apart from each other.
-TEST_F(BreadcrumbPersistentStorageManagerTest, PersistManyEventsOverTime) {
-  std::string event;
-  unsigned long event_count = 0;
-  while (event_count < kEventCountTooManyForPersisting) {
-    event = base::StringPrintf("event %lu", event_count);
-    breadcrumb_manager_service_->AddEvent(event);
-    task_env_.FastForwardBy(base::Seconds(1));
-
-    event_count++;
-  }
-
-  // Advance clock to trigger writing final events.
-  task_env_.FastForwardBy(base::Minutes(1));
-
-  __block bool events_received = false;
-  persistent_storage_->GetStoredEvents(
-      base::BindOnce(^(std::vector<std::string> events) {
-        ASSERT_GT(events.size(), 0ul);
-        EXPECT_LT(events.size(), event_count);
-
-        EXPECT_TRUE(ValidatePersistedEvents(event, events));
-
-        events_received = true;
-      }));
-
-  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
-    base::RunLoop().RunUntilIdle();
-    return events_received;
-  }));
-}
-
-// Ensures that old events are removed from the persisted file when old buckets
-// are dropped.
-TEST_F(BreadcrumbPersistentStorageManagerTest,
-       OldEventsRemovedFromPersistedFile) {
-  std::string event;
-  unsigned long event_counter = 0;
-  const int kNumEventsPerBucket = 200;
-  while (event_counter < kNumEventsPerBucket * 3) {
-    event = base::StringPrintf("event %lu", event_counter);
-    breadcrumb_manager_service_->AddEvent(event);
-    event_counter++;
-
-    if (event_counter % kNumEventsPerBucket == 0) {
-      task_env_.FastForwardBy(base::Hours(1));
-    }
-  }
-
-  // Advance clock to trigger writing final events.
-  task_env_.FastForwardBy(base::Minutes(1));
-
-  __block bool events_received = false;
-  persistent_storage_->GetStoredEvents(
-      base::BindOnce(^(std::vector<std::string> events) {
-        // The exact number of events could vary based on changes in the
-        // implementation. The important part of this test is to verify that a
-        // single event bucket will not grow unbounded and it will be limited to
-        // a value smaller than the overall total number of events which have
-        // been logged.
-        EXPECT_LT(events.size(), event_counter);
-
-        EXPECT_TRUE(ValidatePersistedEvents(event, events));
-
-        events_received = true;
-      }));
-
-  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
-    base::RunLoop().RunUntilIdle();
-    return events_received;
-  }));
-}
-
-// Ensures that events are read correctly if the persisted file becomes
-// corrupted by losing the EOF token or if kPersistedFilesizeInBytes is
-// reduced.
-TEST_F(BreadcrumbPersistentStorageManagerTest,
-       GetStoredEventsAfterFilesizeReduction) {
-  const base::FilePath breadcrumbs_file_path =
-      GetBreadcrumbPersistentStorageFilePath(scoped_temp_directory_.GetPath());
-
-  auto file = std::make_unique<base::File>(
-      breadcrumbs_file_path,
-      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  ASSERT_TRUE(file->IsValid());
-
-  // Simulate an old persisted file larger than the current one.
-  const size_t old_filesize = kPersistedFilesizeInBytes * 1.2;
-  std::string past_breadcrumbs;
-  unsigned long written_events = 0;
-  while (past_breadcrumbs.length() < old_filesize) {
-    past_breadcrumbs += "08:27 event\n";
-    written_events++;
-  }
-
-  ASSERT_TRUE(file->WriteAndCheck(
-      /*offset=*/0, base::as_bytes(base::make_span(past_breadcrumbs))));
-  ASSERT_TRUE(file->Flush());
-  file->Close();
-
-  __block bool events_received = false;
-  persistent_storage_->GetStoredEvents(
-      base::BindOnce(^(std::vector<std::string> events) {
-        EXPECT_GT(events.size(), 1ul);
-        EXPECT_LT(events.size(), written_events);
-        events_received = true;
-      }));
-
-  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
-    base::RunLoop().RunUntilIdle();
-    return events_received;
-  }));
-}
-
-using BreadcrumbPersistentStorageManagerFilenameTest = PlatformTest;
-
-TEST_F(BreadcrumbPersistentStorageManagerFilenameTest,
-       MigrateOldBreadcrumbFiles) {
-  web::WebTaskEnvironment task_env;
-  base::ScopedTempDir scoped_temp_directory;
-  ASSERT_TRUE(scoped_temp_directory.CreateUniqueTempDir());
-  base::FilePath directory_name = scoped_temp_directory.GetPath();
-
-  // Create breadcrumb file and temp file with old filenames.
-  const base::FilePath old_breadcrumb_file_path =
-      breadcrumb_persistent_storage_util::
-          GetOldBreadcrumbPersistentStorageFilePath(directory_name);
-  const base::FilePath old_temp_file_path = breadcrumb_persistent_storage_util::
-      GetOldBreadcrumbPersistentStorageTempFilePath(directory_name);
-  base::File old_breadcrumb_file(
-      old_breadcrumb_file_path,
-      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  base::File old_temp_file(old_temp_file_path, base::File::FLAG_CREATE_ALWAYS |
-                                                   base::File::FLAG_WRITE);
-  ASSERT_TRUE(old_breadcrumb_file.IsValid());
-  ASSERT_TRUE(old_temp_file.IsValid());
-  old_temp_file.Close();
-
-  // Write some test data to the breadcrumb file.
-  const std::string test_data = "breadcrumb file test data";
-  ASSERT_NE(-1,
-            old_breadcrumb_file.Write(0, test_data.c_str(), test_data.size()));
-  old_breadcrumb_file.Close();
-
-  BreadcrumbPersistentStorageManager persistent_storage(
-      directory_name, old_breadcrumb_file_path, old_temp_file_path);
-  task_env.RunUntilIdle();
-
-  // The old files should have been removed, and the new breadcrumb file should
-  // be present.
-  EXPECT_FALSE(base::PathExists(old_breadcrumb_file_path));
-  EXPECT_FALSE(base::PathExists(old_temp_file_path));
-  base::File new_file(GetBreadcrumbPersistentStorageFilePath(directory_name),
-                      base::File::FLAG_OPEN | base::File::FLAG_READ);
-  EXPECT_TRUE(new_file.IsValid());
-  const size_t test_data_size = test_data.size();
-  char new_file_data[test_data_size];
-  EXPECT_EQ(static_cast<int>(test_data_size),
-            new_file.Read(0, new_file_data, test_data_size));
-  EXPECT_EQ(test_data, std::string(new_file_data, test_data_size));
-}
-
-}  // namespace breadcrumbs
diff --git a/ios/chrome/browser/crash_report/crash_helper.h b/ios/chrome/browser/crash_report/crash_helper.h
index bc14a29..ac0cb67 100644
--- a/ios/chrome/browser/crash_report/crash_helper.h
+++ b/ios/chrome/browser/crash_report/crash_helper.h
@@ -18,16 +18,15 @@
 // Enables/Disables crash handling.
 void SetEnabled(bool enabled);
 
-// Enable/Disable uploading crash reports.
-void SetUploadingEnabled(bool enabled);
+// Process and begin uploading pending crash reports if application is active.
+// If application state is inactive or backgrounded, this is a no-op. Can be
+// called multiple times, but will only take effect the first time (when app
+// state is active) for Crashpad. For Breakpad, this can be called to start
+// uploads and restart uploads after -PauseBreakpadUploads() is called.
+void UploadCrashReports();
 
-// Sets the user preferences related to Breakpad and cache them to be used on
-// next startup to check if safe mode must be started.
-void SetUserEnabledUploading(bool enabled);
-
-// For breakpad, if |after_upgrade| is true, delete all pending reports.  For
-// crashpad, regardless of |after_upgrade|, process pending intermediate dumps.
-void CleanupCrashReports(bool after_upgrade);
+// For breakpad, it is necessary to pause uploads when entering the background.
+void PauseBreakpadUploads();
 
 // Process any pending crashpad reports, and mark them as
 // 'uploaded_in_recovery_mode'.
diff --git a/ios/chrome/browser/crash_report/crash_helper.mm b/ios/chrome/browser/crash_report/crash_helper.mm
index 2a8cfb7d..30f0e7f 100644
--- a/ios/chrome/browser/crash_report/crash_helper.mm
+++ b/ios/chrome/browser/crash_report/crash_helper.mm
@@ -7,6 +7,7 @@
 #import <UIKit/UIKit.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <sys/stat.h>
 #include <sys/sysctl.h>
 
 #include "base/auto_reset.h"
@@ -47,13 +48,25 @@
 const char kUptimeAtRestoreInMs[] = "uptime_at_restore_in_ms";
 const char kUploadedInRecoveryMode[] = "uploaded_in_recovery_mode";
 
-void DeleteAllReportsInDirectory(base::FilePath directory) {
+// Delete breakpad reports after 60 days.
+void DeleteOldReportsInDirectory(base::FilePath directory) {
   base::FileEnumerator enumerator(directory, false,
                                   base::FileEnumerator::FILES);
   base::FilePath cur_file;
   while (!(cur_file = enumerator.Next()).value().empty()) {
-    if (cur_file.BaseName().value() != kReporterLogFilename)
-      base::DeleteFile(cur_file);
+    if (cur_file.BaseName().value() != kReporterLogFilename) {
+      time_t now = time(nullptr);
+      struct stat st;
+      if (lstat(cur_file.value().c_str(), &st) != 0) {
+        continue;
+      }
+
+      // 60 days.
+      constexpr time_t max_breakpad_report_age_sec = 60 * 60 * 24 * 60;
+      if (st.st_mtime <= now - max_breakpad_report_age_sec) {
+        base::DeleteFile(cur_file);
+      }
+    }
   }
 }
 
@@ -171,6 +184,10 @@
 }
 
 void SetEnabled(bool enabled) {
+  // Caches the uploading flag in NSUserDefaults, so that we can access the
+  // value immediately on startup, such as in safe mode or extensions.
+  crash_helper::common::SetUserEnabledUploading(enabled);
+
   // It is necessary to always call |MainThreadFreezeDetector setEnabled| as
   // the function will update its preference based on finch.
   [[MainThreadFreezeDetector sharedInstance] setEnabled:enabled];
@@ -179,6 +196,7 @@
   // here, because if Crashpad fails to init, do not unintentionally enable
   // breakpad.
   if (common::CanUseCrashpad()) {
+    crash_reporter::SetUploadConsent(enabled);
     return;
   }
 
@@ -192,74 +210,38 @@
   }
 }
 
-// Breakpad only.
-void SetBreakpadUploadingEnabled(bool enabled) {
-  if (!crash_reporter::IsBreakpadRunning())
-    return;
-  if (enabled) {
+void UploadCrashReports() {
+  if (crash_reporter::IsCrashpadRunning()) {
+    static dispatch_once_t once_token;
+    dispatch_once(&once_token, ^{
+      base::ThreadPool::PostTask(
+          FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+          base::BindOnce(&ProcessIntermediateDumps));
+      return;
+    });
+  }
+
+  if (crash_reporter::IsBreakpadRunning()) {
     static dispatch_once_t once_token;
     dispatch_once(&once_token, ^{
       [[BreakpadController sharedInstance]
           setUploadCallback:UploadResultHandler];
+
+      // Clean old breakpad files here. Breakpad-only as Crashpad has it's own
+      // database cleaner.
+      base::FilePath crash_directory;
+      base::PathService::Get(ios::DIR_CRASH_DUMPS, &crash_directory);
+      base::ThreadPool::PostTask(
+          FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+          base::BindOnce(&DeleteOldReportsInDirectory, crash_directory));
     });
-  }
-  [[BreakpadController sharedInstance] setUploadingEnabled:enabled];
-}
-
-// Caches the uploading flag in NSUserDefaults, so that we can access the value
-// immediately on startup, such as in safe mode or extensions.
-void SetUserEnabledUploading(bool uploading_enabled) {
-  [app_group::GetGroupUserDefaults()
-      setBool:uploading_enabled ? YES : NO
-       forKey:base::SysUTF8ToNSString(
-                  common::kCrashReportsUploadingEnabledKey)];
-}
-
-void SetUploadingEnabled(bool enabled) {
-  if (enabled && [UIApplication sharedApplication].applicationState ==
-                     UIApplicationStateInactive) {
-    return;
-  }
-
-  if (crash_reporter::IsCrashpadRunning()) {
-    crash_reporter::SetUploadConsent(enabled);
-    [[MainThreadFreezeDetector sharedInstance] prepareCrashReportsForUpload:^(){
-    }];
-    return;
-  }
-
-  if (common::CanUseCrashpad()) {
-    return;
-  }
-
-  if ([MainThreadFreezeDetector sharedInstance].canUploadBreakpadCrashReports) {
-    SetBreakpadUploadingEnabled(enabled);
-  } else {
-    [[MainThreadFreezeDetector sharedInstance]
-        prepareCrashReportsForUpload:^() {
-          SetBreakpadUploadingEnabled(enabled);
-        }];
+    [[BreakpadController sharedInstance] setUploadingEnabled:YES];
   }
 }
 
-void CleanupCrashReports(BOOL after_upgrade) {
-  if (crash_reporter::IsCrashpadRunning()) {
-    base::ThreadPool::PostTask(
-        FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(&ProcessIntermediateDumps));
-    return;
-  }
-
-  if (common::CanUseCrashpad()) {
-    return;
-  }
-
-  if (after_upgrade) {
-    base::FilePath crash_directory;
-    base::PathService::Get(ios::DIR_CRASH_DUMPS, &crash_directory);
-    base::ThreadPool::PostTask(
-        FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-        base::BindOnce(&DeleteAllReportsInDirectory, crash_directory));
+void PauseBreakpadUploads() {
+  if (crash_reporter::IsBreakpadRunning()) {
+    [[BreakpadController sharedInstance] setUploadingEnabled:NO];
   }
 }
 
diff --git a/ios/chrome/browser/crash_report/crash_helper_unittest.mm b/ios/chrome/browser/crash_report/crash_helper_unittest.mm
index ff8e28e..ac702e3 100644
--- a/ios/chrome/browser/crash_report/crash_helper_unittest.mm
+++ b/ios/chrome/browser/crash_report/crash_helper_unittest.mm
@@ -111,22 +111,22 @@
 }
 
 TEST_F(BreakpadHelperTest, IsUploadingEnabled) {
-  crash_helper::SetUserEnabledUploading(true);
+  crash_helper::common::SetUserEnabledUploading(true);
   EXPECT_TRUE(crash_helper::common::UserEnabledUploading());
   crash_helper::SetEnabled(false);
-  EXPECT_TRUE(crash_helper::common::UserEnabledUploading());
+  EXPECT_FALSE(crash_helper::common::UserEnabledUploading());
   [[mock_breakpad_controller_ expect] start:NO];
   crash_helper::SetEnabled(true);
   EXPECT_TRUE(crash_helper::common::UserEnabledUploading());
 
-  crash_helper::SetUserEnabledUploading(false);
+  crash_helper::common::SetUserEnabledUploading(false);
   EXPECT_FALSE(crash_helper::common::UserEnabledUploading());
   [[mock_breakpad_controller_ expect] stop];
   crash_helper::SetEnabled(false);
   EXPECT_FALSE(crash_helper::common::UserEnabledUploading());
   [[mock_breakpad_controller_ expect] start:NO];
   crash_helper::SetEnabled(true);
-  EXPECT_FALSE(crash_helper::common::UserEnabledUploading());
+  EXPECT_TRUE(crash_helper::common::UserEnabledUploading());
 }
 
 TEST_F(BreakpadHelperTest, StartUploadingReportsInRecoveryMode) {
diff --git a/ios/chrome/browser/crash_report/main_thread_freeze_detector.h b/ios/chrome/browser/crash_report/main_thread_freeze_detector.h
index 1086b9f..abbeee0 100644
--- a/ios/chrome/browser/crash_report/main_thread_freeze_detector.h
+++ b/ios/chrome/browser/crash_report/main_thread_freeze_detector.h
@@ -27,9 +27,6 @@
 // The result of the previous session. If this is true, the last time the
 // application was terminated, main thread was not responding.
 @property(nonatomic, readonly) BOOL lastSessionEndedFrozen;
-// Whether the UTE report from last session has been processed and it is now
-// possible to start crash report upload.
-@property(nonatomic, readonly) BOOL canUploadBreakpadCrashReports;
 // Starts the watchdog of the main thread.
 - (void)start;
 // Stops the watchdog of the main thread.
@@ -37,19 +34,6 @@
 // Enables or disables the main thread watchdog. This will also start or stop
 // the monitoring of the main thread.
 - (void)setEnabled:(BOOL)enabled;
-// Prepare the UTE report before the crash handler starts to uploading them.
-// If using Breakpad:
-// Call completion on main thread when complete. If this is called multiple
-// times before |completion| is called, only the latest |completion| block will
-// be called. The function will queue the UTE report to be uploaded if there is
-// no newer crash report in the Breakpad directory.
-// If using Crashpad:
-// Completion unused by Crashpad. Because -prepareCrashReportsForUpload is
-// called early enough on startup, Crashpad has not processed crash intermediate
-// dumps yet.  Since the UTE directory should be cleared to avoid duplicate UTE
-// reports, move any UTE reports from the previous session to a to-be-processed
-// location to be used by -processIntermediateDumps.
-- (void)prepareCrashReportsForUpload:(ProceduralBlock)completion;
 // Crashpad only. Tell Crashpad to process UTE intermediate dumps if there are
 // no newer crash reports. This should only be called after
 // crash_reporter::ProcessIntermediateDumps(), otherwise there would be no
diff --git a/ios/chrome/browser/crash_report/main_thread_freeze_detector.mm b/ios/chrome/browser/crash_report/main_thread_freeze_detector.mm
index 52202f3..a3bb8892 100644
--- a/ios/chrome/browser/crash_report/main_thread_freeze_detector.mm
+++ b/ios/chrome/browser/crash_report/main_thread_freeze_detector.mm
@@ -86,9 +86,6 @@
   // The directory containing UTE crash reports eligible for crashpad
   // processing.
   NSString* _UTEPendingCrashpadDirectory;
-  // The block to call (on main thread) once the UTE report is restored in the
-  // breakpad directory.
-  ProceduralBlock _restorationCompletion;
 }
 
 + (instancetype)sharedInstance {
@@ -152,6 +149,9 @@
 - (void)setEnabled:(BOOL)enabled {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
+    dispatch_async(_freezeDetectionQueue, ^{
+      [self handleLastSessionReport];
+    });
     if (_lastSessionEndedFrozen) {
       LogRecoveryTime(base::Seconds(0));
     }
@@ -209,16 +209,12 @@
 }
 
 - (void)cleanAndRunInFreezeDetectionQueue {
-  if (_canUploadBreakpadCrashReports) {
-    // If the prevous session is not processed yet, do not delete the directory.
-    // It will be cleared on completion of processing the previous session.
-    NSFileManager* fileManager = [[NSFileManager alloc] init];
-    [fileManager removeItemAtPath:_UTEDirectory error:nil];
-    [fileManager createDirectoryAtPath:_UTEDirectory
-           withIntermediateDirectories:NO
-                            attributes:nil
-                                 error:nil];
-  }
+  NSFileManager* fileManager = [[NSFileManager alloc] init];
+  [fileManager removeItemAtPath:_UTEDirectory error:nil];
+  [fileManager createDirectoryAtPath:_UTEDirectory
+         withIntermediateDirectories:NO
+                          attributes:nil
+                               error:nil];
   [self runInFreezeDetectionQueue];
 }
 
@@ -347,17 +343,6 @@
   [fileManager removeItemAtPath:_UTEPendingCrashpadDirectory error:nil];
 }
 
-- (void)prepareCrashReportsForUpload:(ProceduralBlock)completion {
-  DCHECK(completion);
-  _restorationCompletion = completion;
-  static dispatch_once_t onceToken;
-  dispatch_once(&onceToken, ^{
-    dispatch_async(_freezeDetectionQueue, ^{
-      [self handleLastSessionReport];
-    });
-  });
-}
-
 - (void)restoreLastSessionReportIfNeeded {
   if (!_lastSessionFreezeInfo) {
     return;
@@ -440,23 +425,13 @@
   NSFileManager* fileManager = [[NSFileManager alloc] init];
   // It is possible that this call will delete a report on the current session.
   // But this is unlikely because |handleLastSessionReport| run on the
-  // |_freezeDetectionQueue| and is called directly from the main thread
-  // |prepareToUpload| which mean that main thread was responding recently.
+  // |_freezeDetectionQueue| and is called directly from setEnabled which means
+  // that main thread was responding recently.
   [fileManager removeItemAtPath:_UTEDirectory error:nil];
   [fileManager createDirectoryAtPath:_UTEDirectory
          withIntermediateDirectories:NO
                           attributes:nil
                                error:nil];
-
-  dispatch_async(dispatch_get_main_queue(), ^{
-    [self handleSessionRestorationCompletion];
-  });
-}
-
-- (void)handleSessionRestorationCompletion {
-  _canUploadBreakpadCrashReports = YES;
-  DCHECK(_restorationCompletion);
-  _restorationCompletion();
 }
 
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
index 6858dbb..652a01d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
@@ -29,6 +29,7 @@
   CGFloat ntpHeight = collectionViewHeight + headerHeight;
   CGFloat minimumHeight =
       ntpHeight - ntp_header::kScrolledToTopOmniboxBottomMargin;
+  CGFloat topSafeArea = self.collectionView.safeAreaInsets.top;
   if (!IsRegularXRegularSizeClass(self.collectionView)) {
     CGFloat toolbarHeight =
         IsSplitToolbarMode(self.collectionView)
@@ -36,7 +37,7 @@
                                         .preferredContentSizeCategory)
             : 0;
     CGFloat additionalHeight =
-        toolbarHeight + self.collectionView.contentInset.bottom;
+        toolbarHeight + topSafeArea + self.collectionView.contentInset.bottom;
     minimumHeight -= additionalHeight;
     ntpHeight += additionalHeight;
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
index 6b8f623..72dec5e 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
+++ b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
@@ -143,6 +143,9 @@
 - (void)recordBrokenNTPHierarchy:
     (BrokenNTPHierarchyRelationship)brokenRelationship;
 
+// Records that the feed is about to be refreshed.
+- (void)recordFeedWillRefresh;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_DISCOVER_FEED_METRICS_RECORDER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
index 388b351..8f15b408 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
@@ -123,6 +123,14 @@
 // User action name for engaging with feed.
 const char kDiscoverFeedUserActionEngaged[] = "ContentSuggestions.Feed.Engaged";
 
+// User action indicating that the feed will refresh.
+const char kFeedWillRefresh[] = "ContentSuggestions.Feed.WillRefresh";
+
+// User action triggered when the NTP view hierarchy was fixed after being
+// detected as broken.
+// TODO(crbug.com/1262536): Remove this when issue is fixed.
+const char kNTPViewHierarchyFixed[] = "NewTabPage.ViewHierarchyFixed";
+
 // Histogram name for the feed engagement types.
 const char kDiscoverFeedEngagementTypeHistogram[] =
     "ContentSuggestions.Feed.EngagementType";
@@ -453,6 +461,11 @@
 
 - (void)recordBrokenNTPHierarchy:(BrokenNTPHierarchyRelationship)relationship {
   base::UmaHistogramEnumeration(kDiscoverFeedBrokenNTPHierarchy, relationship);
+  base::RecordAction(base::UserMetricsAction(kNTPViewHierarchyFixed));
+}
+
+- (void)recordFeedWillRefresh {
+  base::RecordAction(base::UserMetricsAction(kFeedWillRefresh));
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index 818a9f9..6273ea0 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -754,14 +754,20 @@
   [self checkIfNTPIsScrollable];
 
   // Hide feed.
-  [self hideFeedFromNTPMenu];
+  // TODO(crbug.com/1194106): Hide feed using feed header menu instead of
+  // manipulating pref directly.
+  [ChromeEarlGreyAppInterface
+      setBoolValue:NO
+       forUserPref:base::SysUTF8ToNSString(feed::prefs::kArticlesListVisible)];
 
   // Check feed label and if NTP is scrollable.
   [self checkFeedLabelForFeedVisible:NO];
   [self checkIfNTPIsScrollable];
 
   // Show feed again.
-  [self showFeedFromNTPMenu];
+  [ChromeEarlGreyAppInterface
+      setBoolValue:YES
+       forUserPref:base::SysUTF8ToNSString(feed::prefs::kArticlesListVisible)];
 
   // Check feed label and if NTP is scrollable.
   [self checkFeedLabelForFeedVisible:YES];
@@ -840,71 +846,6 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-#pragma mark - New Tab menu tests
-
-// Tests the "new search" menu item from the new tab menu.
-- (void)testNewSearchFromNewTabMenu {
-  if ([ChromeEarlGrey isIPadIdiom]) {
-    EARL_GREY_TEST_SKIPPED(@"New Search is only available in phone layout.");
-  }
-
-  [ChromeEarlGreyUI openNewTabMenu];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(kToolsMenuSearch)]
-      performAction:grey_tap()];
-  GREYWaitForAppToIdle(@"App failed to idle");
-
-  // Check that there's now a new tab, that the new (second) tab is the active
-  // one, and the that the omnibox is first responder.
-  [ChromeEarlGrey waitForMainTabCount:2];
-
-  GREYAssertEqual(1, [ChromeEarlGrey indexOfActiveNormalTab],
-                  @"Tab 1 should be active after starting a new search.");
-
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_notVisible()];
-  // Fakebox should be covered.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
-      assertWithMatcher:grey_notVisible()];
-  GREYWaitForAppToIdle(@"App failed to idle");
-}
-
-// Tests the "new search" menu item from the new tab menu after disabling the
-// feed.
-- (void)testNewSearchFromNewTabMenuAfterTogglingFeed {
-  if ([ChromeEarlGrey isIPadIdiom]) {
-    EARL_GREY_TEST_SKIPPED(@"New Search is only available in phone layout.");
-  }
-
-  // Hide feed.
-  [self hideFeedFromNTPMenu];
-
-  [ChromeEarlGreyUI openNewTabMenu];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(kToolsMenuSearch)]
-      performAction:grey_tap()];
-  GREYWaitForAppToIdle(@"App failed to idle");
-
-  // Check that there's now a new tab, that the new (third) tab is the active
-  // one, and the that the omnibox is first responder.
-  [ChromeEarlGrey waitForMainTabCount:2];
-
-  GREYAssertEqual(1, [ChromeEarlGrey indexOfActiveNormalTab],
-                  @"Tab 1 should be active after starting a new search.");
-
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::Omnibox()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::DefocusedLocationView()]
-      assertWithMatcher:grey_notVisible()];
-
-  // Fakebox should be covered.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::FakeOmnibox()]
-      assertWithMatcher:grey_notVisible()];
-  GREYWaitForAppToIdle(@"App failed to idle");
-}
-
 #pragma mark - Helpers
 
 - (void)addMostVisitedTile {
@@ -980,40 +921,4 @@
       performAction:grey_scrollToContentEdge(kGREYContentEdgeTop)];
 }
 
-- (void)showFeedFromNTPMenu {
-  bool feed_visible =
-      [ChromeEarlGrey userBooleanPref:feed::prefs::kArticlesListVisible];
-  GREYAssertFalse(feed_visible, @"Expect feed to be hidden!");
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          kNTPFeedHeaderButtonIdentifier)]
-      performAction:grey_tap()];
-
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NTPFeedMenuEnableButton()]
-      performAction:grey_tap()];
-
-  feed_visible =
-      [ChromeEarlGrey userBooleanPref:feed::prefs::kArticlesListVisible];
-  GREYAssertTrue(feed_visible, @"Expect feed to be visible!");
-}
-
-- (void)hideFeedFromNTPMenu {
-  bool feed_visible =
-      [ChromeEarlGrey userBooleanPref:feed::prefs::kArticlesListVisible];
-  GREYAssertTrue(feed_visible, @"Expect feed to be visible!");
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          kNTPFeedHeaderButtonIdentifier)]
-      performAction:grey_tap()];
-
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::NTPFeedMenuDisableButton()]
-      performAction:grey_tap()];
-
-  feed_visible =
-      [ChromeEarlGrey userBooleanPref:feed::prefs::kArticlesListVisible];
-  GREYAssertFalse(feed_visible, @"Expect feed to be hidden!");
-}
-
 @end
diff --git a/ios/chrome/browser/ui/safe_mode/BUILD.gn b/ios/chrome/browser/ui/safe_mode/BUILD.gn
index a3322caa..9363a76 100644
--- a/ios/chrome/browser/ui/safe_mode/BUILD.gn
+++ b/ios/chrome/browser/ui/safe_mode/BUILD.gn
@@ -91,6 +91,7 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/crash_report",
+    "//ios/chrome/common/crash_report",
     "//ios/chrome/test/ocmock",
     "//ios/testing:block_swizzler",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm b/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
index 0a36e17..e13ae59f 100644
--- a/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/safe_mode/safe_mode_view_controller_unittest.mm
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/ui/safe_mode/safe_mode_view_controller.h"
+#include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#include "base/test/task_environment.h"
 #import "ios/chrome/browser/crash_report/crash_helper.h"
 #import "ios/chrome/browser/crash_report/main_thread_freeze_detector.h"
+#include "ios/chrome/common/crash_report/crash_helper.h"
 #import "ios/chrome/test/ocmock/OCMockObject+BreakpadControllerTesting.h"
 #import "ios/testing/scoped_block_swizzler.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -48,6 +51,7 @@
   }
 
  protected:
+  base::test::TaskEnvironment task_environment;
   id mock_breakpad_controller_;
   std::unique_ptr<ScopedBlockSwizzler>
       breakpad_controller_shared_instance_swizzler_;
@@ -58,41 +62,13 @@
 // reports to upload. +[SafeModeViewController hasSuggestions] does not depend
 // on the value of crash_helper::IsEnabled or
 // crash_helper::IsUploadingEnabled.
-// TODO(crbug.com/1173776): The test fails on device.
-#if TARGET_IPHONE_SIMULATOR
-#define MAYBE_HasSuggestions HasSuggestions
-#else
-#define MAYBE_HasSuggestions DISABLED_HasSuggestions
-#endif
-TEST_F(SafeModeViewControllerTest, MAYBE_HasSuggestions) {
+TEST_F(SafeModeViewControllerTest, HasSuggestions) {
   // Test when crash reporter is disabled.
-  crash_helper::SetUserEnabledUploading(false);
-  EXPECT_FALSE([SafeModeViewController hasSuggestions]);
-
-  crash_helper::SetUploadingEnabled(false);
-  EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
-      // Calling SetUploadingEnabled() for the first time kicks off several
-      // asynchronous calls that ultimately result in MainThreadFreezeDetector's
-      // |-canUploadBreakpadCrashReports| being flipped to YES.  Subsequent
-      // calls will perform synchronously after |canUploadBreakpadCrashReports|
-      // is YES.  The OCMock verification calls below expect these selectors to
-      // be called synchronously, so wait until |canUploadBreakpadCrashReports|
-      // is YES before continuing the test.
-      // TODO(crbug.com/931826): Remove timing assumptions for the OCMock
-      // verification calls below.
-      base::test::ios::kWaitForUIElementTimeout, ^bool {
-        return [MainThreadFreezeDetector sharedInstance]
-            .canUploadBreakpadCrashReports;
-      }));
-  EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
-  EXPECT_FALSE([SafeModeViewController hasSuggestions]);
-
-  crash_helper::SetUploadingEnabled(true);
-  EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
+  crash_helper::common::SetUserEnabledUploading(false);
   EXPECT_FALSE([SafeModeViewController hasSuggestions]);
 
   // Test when crash reporter is enabled.
-  crash_helper::SetUserEnabledUploading(true);
+  crash_helper::common::SetUserEnabledUploading(true);
   EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
   [mock_breakpad_controller_ cr_expectGetCrashReportCount:0];
   EXPECT_FALSE([SafeModeViewController hasSuggestions]);
@@ -106,7 +82,7 @@
   crash_helper::SetEnabled(true);
 
   [[mock_breakpad_controller_ expect] setUploadingEnabled:NO];
-  crash_helper::SetUploadingEnabled(false);
+  crash_helper::PauseBreakpadUploads();
   EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
   [mock_breakpad_controller_ cr_expectGetCrashReportCount:0];
   EXPECT_FALSE([SafeModeViewController hasSuggestions]);
@@ -121,9 +97,7 @@
   [[mock_breakpad_controller_ expect]
       setUploadCallback:reinterpret_cast<BreakpadUploadCompletionCallback>(
                             [OCMArg anyPointer])];
-  crash_helper::SetUploadingEnabled(true);
-  EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
-
+  crash_helper::UploadCrashReports();
   [mock_breakpad_controller_ cr_expectGetCrashReportCount:0];
   EXPECT_FALSE([SafeModeViewController hasSuggestions]);
   EXPECT_OCMOCK_VERIFY(mock_breakpad_controller_);
diff --git a/ios/chrome/browser/ui/settings/settings_app_interface.h b/ios/chrome/browser/ui/settings/settings_app_interface.h
index 6b6877c..9b58706 100644
--- a/ios/chrome/browser/ui/settings/settings_app_interface.h
+++ b/ios/chrome/browser/ui/settings/settings_app_interface.h
@@ -26,9 +26,6 @@
 // Allows turning on and off metrics reporting.
 + (void)setMetricsReportingEnabled:(BOOL)reportingEnabled;
 
-// Allows simulating turning the cellular network on/off.
-+ (void)setCellularNetworkEnabled:(BOOL)cellularNetworkEnabled;
-
 // YES if breakpad crash collection is enabled.
 + (BOOL)isBreakpadEnabled;
 
diff --git a/ios/chrome/browser/ui/settings/settings_app_interface.mm b/ios/chrome/browser/ui/settings/settings_app_interface.mm
index 9b015835..744ea3b0 100644
--- a/ios/chrome/browser/ui/settings/settings_app_interface.mm
+++ b/ios/chrome/browser/ui/settings/settings_app_interface.mm
@@ -75,13 +75,6 @@
   chrome_test_util::WaitForBreakpadQueue();
 }
 
-+ (void)setCellularNetworkEnabled:(BOOL)cellularNetworkEnabled {
-  chrome_test_util::SetWWANStateTo(cellularNetworkEnabled);
-  // Breakpad uses dispatch_async to update its state. Wait to get to a
-  // consistent state.
-  chrome_test_util::WaitForBreakpadQueue();
-}
-
 + (BOOL)isBreakpadEnabled {
   return chrome_test_util::IsBreakpadEnabled();
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm
index c46f50e2..898baa6 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_bottom_toolbar.mm
@@ -334,6 +334,15 @@
 }
 
 - (void)updateLayout {
+  // Search mode doesn't have bottom toolbar or floating buttons, Handle it and
+  // return early in that case.
+  if (self.mode == TabGridModeSearch) {
+    [NSLayoutConstraint deactivateConstraints:_compactConstraints];
+    [_toolbar removeFromSuperview];
+    [NSLayoutConstraint deactivateConstraints:_floatingConstraints];
+    [_largeNewTabButton removeFromSuperview];
+    return;
+  }
   _largeNewTabButtonBottomAnchor.constant =
       -kTabGridFloatingButtonVerticalInset;
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
index e922e7b..825d1a7 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
@@ -227,8 +227,8 @@
   if (![self shouldUseCompactLayout:traitCollection])
     widthModifier = kTabGridSearchBarNonCompactWidthRatioModifier;
 
-  float cancelWidth = [_selectAllButton.title sizeWithAttributes:nil].width;
-  float barWidth =
+  CGFloat cancelWidth = [_selectAllButton.title sizeWithAttributes:nil].width;
+  CGFloat barWidth =
       (self.bounds.size.width - kSearchBarTrailingSpace - cancelWidth) *
       kTabGridSearchBarWidthRatio * widthModifier;
   // Update the search bar size based on the container size.
@@ -249,10 +249,13 @@
   UIBarButtonItem* trailingButton = _doneButton;
   _selectionModeFixedSpace.width = 0;
   if ([self shouldUseCompactLayout:traitCollection]) {
-    if (IsTabsSearchEnabled() && _mode == TabGridModeNormal)
+    if (IsTabsSearchEnabled() && _mode == TabGridModeNormal &&
+        _page != TabGridPageRemoteTabs) {
       _leadingButton = _searchButton;
-    else
+    } else {
       _leadingButton = _spaceItem;
+    }
+
     if (_mode == TabGridModeSelection) {
       // In the selection mode, Done button is much smaller than SelectAll
       // we need to calculate the difference on the width and use it as a
@@ -296,7 +299,8 @@
     _leadingButton = _selectAllButton;
   }
 
-  if (IsTabsSearchEnabled() && _mode == TabGridModeNormal) {
+  if (IsTabsSearchEnabled() && _mode == TabGridModeNormal &&
+      _page != TabGridPageRemoteTabs) {
     [self setItems:@[
       _leadingButton, _iconButtonAdditionalSpaceItem, _searchButton, _spaceItem,
       centralItem, _spaceItem, trailingButton
diff --git a/ios/chrome/common/crash_report/crash_helper.h b/ios/chrome/common/crash_report/crash_helper.h
index 4d21f4e..973a335 100644
--- a/ios/chrome/common/crash_report/crash_helper.h
+++ b/ios/chrome/common/crash_report/crash_helper.h
@@ -10,10 +10,6 @@
 namespace crash_helper {
 namespace common {
 
-// Key in NSUserDefaults for a Boolean value that stores whether to upload
-// crash reports.
-extern const char kCrashReportsUploadingEnabledKey[];
-
 // Key in NSUserDefaults for a Boolean value that stores the last feature
 // value of kCrashpadIOS.
 extern const char kCrashpadStartOnNextRun[];
@@ -21,6 +17,10 @@
 // Returns true if uploading crash reports is enabled in the settings.
 bool UserEnabledUploading();
 
+// Sets the user preferences related to uploading crash reports and cache them
+// to be used on next startup to check if safe mode must be started.
+void SetUserEnabledUploading(bool enabled);
+
 // Check and cache the GroupUserDefaults value synced from the associated
 // kCrashpadIOS feature.
 bool CanUseCrashpad();
diff --git a/ios/chrome/common/crash_report/crash_helper.mm b/ios/chrome/common/crash_report/crash_helper.mm
index e37c1dd8..78324ef 100644
--- a/ios/chrome/common/crash_report/crash_helper.mm
+++ b/ios/chrome/common/crash_report/crash_helper.mm
@@ -30,6 +30,13 @@
       boolForKey:base::SysUTF8ToNSString(kCrashReportsUploadingEnabledKey)];
 }
 
+void SetUserEnabledUploading(bool enabled) {
+  [app_group::GetGroupUserDefaults()
+      setBool:enabled ? YES : NO
+       forKey:base::SysUTF8ToNSString(
+                  common::kCrashReportsUploadingEnabledKey)];
+}
+
 bool CanUseCrashpad() {
   static bool can_use_crashpad = [app_group::GetGroupUserDefaults()
       boolForKey:base::SysUTF8ToNSString(kCrashpadStartOnNextRun)];
diff --git a/ios/chrome/test/app/chrome_test_util.h b/ios/chrome/test/app/chrome_test_util.h
index e2310dd7..cca7726 100644
--- a/ios/chrome/test/app/chrome_test_util.h
+++ b/ios/chrome/test/app/chrome_test_util.h
@@ -71,9 +71,6 @@
                         const char* pref_name,
                         int value);
 
-// Sets the state of using cellular network.
-void SetWWANStateTo(bool value);
-
 // Sets the state of first launch.
 void SetFirstLaunchStateTo(bool value);
 
diff --git a/ios/chrome/test/app/chrome_test_util.mm b/ios/chrome/test/app/chrome_test_util.mm
index fe1b59a..1336be6 100644
--- a/ios/chrome/test/app/chrome_test_util.mm
+++ b/ios/chrome/test/app/chrome_test_util.mm
@@ -201,14 +201,6 @@
   pref.SetValue(value);
 }
 
-void SetWWANStateTo(bool value) {
-  MainController* mainController = chrome_test_util::GetMainController();
-  net::NetworkChangeNotifier::ConnectionType connectionType =
-      value ? net::NetworkChangeNotifier::CONNECTION_4G
-            : net::NetworkChangeNotifier::CONNECTION_WIFI;
-  [mainController.metricsMediator connectionTypeChanged:connectionType];
-}
-
 void SetFirstLaunchStateTo(bool value) {
   [[PreviousSessionInfo sharedInstance] setIsFirstSessionAfterUpgrade:value];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
index 1858f44..ac45af54 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
@@ -54,11 +54,6 @@
 // Sets and Leaves the root matcher to the given window with |windowNumber|.
 - (void)openSettingsMenuInWindowWithNumber:(int)windowNumber;
 
-// Makes the toolbar visible by swiping downward, if necessary. Then long-
-// presses on the New Tab menu button. At least one tab needs to be open and
-// visible when calling this method.
-- (void)openNewTabMenu;
-
 // Scrolls to find the button in the Tools menu with the corresponding
 // |buttonMatcher|, and then taps it. If |buttonMatcher| is not found, or
 // the Tools menu is not open when this is called there will be a GREYAssert.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
index 089763d0..d2e46e3 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -126,19 +126,6 @@
   [self tapToolsMenuButton:SettingsMenuButton()];
 }
 
-- (void)openNewTabMenu {
-  // TODO(crbug.com/639524): Add logic to ensure the app is in the correct
-  // state, for example DCHECK if no tabs are displayed.
-  [[[EarlGrey
-      selectElementWithMatcher:grey_allOf(chrome_test_util::NewTabButton(),
-                                          grey_sufficientlyVisible(), nil)]
-         usingSearchAction:grey_swipeSlowInDirection(kGREYDirectionDown)
-      onElementWithMatcher:chrome_test_util::WebStateScrollViewMatcher()]
-      performAction:grey_longPress()];
-  // TODO(crbug.com/639517): Add webViewScrollView matcher so we don't have
-  // to always find it.
-}
-
 - (void)tapToolsMenuButton:(id<GREYMatcher>)buttonMatcher {
   ScopedDisableTimerTracking disabler;
   id<GREYMatcher> interactableSettingsButton =
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 02ae426b..0ab8b4a 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -132,9 +132,6 @@
 // Matcher for Tools menu button.
 id<GREYMatcher> ToolsMenuButton();
 
-// Matcher for the New Tab button, which can be long-pressed for a menu.
-id<GREYMatcher> NewTabButton();
-
 // Matcher for the Share menu button.
 id<GREYMatcher> ShareButton();
 
@@ -343,12 +340,6 @@
 // Returns matcher for the NTP view when the user is in incognito mode.
 id<GREYMatcher> NTPIncognitoView();
 
-// Returns matcher for the NTP Feed menu button which enables the feed.
-id<GREYMatcher> NTPFeedMenuEnableButton();
-
-// Returns matcher for the NTP Feed menu button which disables the feed.
-id<GREYMatcher> NTPFeedMenuDisableButton();
-
 // Returns matcher for the warning message while filling in payment requests.
 id<GREYMatcher> WarningMessageView();
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 35858632..2c2c61a 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -170,10 +170,6 @@
   return [ChromeMatchersAppInterface toolsMenuButton];
 }
 
-id<GREYMatcher> NewTabButton() {
-  return [ChromeMatchersAppInterface openNewTabButton];
-}
-
 id<GREYMatcher> ShareButton() {
   return [ChromeMatchersAppInterface shareButton];
 }
@@ -435,14 +431,6 @@
   return [ChromeMatchersAppInterface ntpIncognitoView];
 }
 
-id<GREYMatcher> NTPFeedMenuEnableButton() {
-  return [ChromeMatchersAppInterface ntpFeedMenuEnableButton];
-}
-
-id<GREYMatcher> NTPFeedMenuDisableButton() {
-  return [ChromeMatchersAppInterface ntpFeedMenuDisableButton];
-}
-
 id<GREYMatcher> WarningMessageView() {
   return [ChromeMatchersAppInterface warningMessageView];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index be81626c..c4f9861 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -125,11 +125,6 @@
 // Matcher for Tools menu button.
 + (id<GREYMatcher>)toolsMenuButton;
 
-// Matcher for the New Tab button, which can be long-pressed for a menu.
-// (This method can't be named +newTabButton, because starting a class method
-// with 'new' implicitly treats it as a constructor).
-+ (id<GREYMatcher>)openNewTabButton;
-
 // Matcher for the Share... button.
 + (id<GREYMatcher>)shareButton;
 
@@ -339,12 +334,6 @@
 // Returns matcher for the NTP view when the user is in incognito mode.
 + (id<GREYMatcher>)ntpIncognitoView;
 
-// Returns matcher for the NTP Feed menu button which enables the feed.
-+ (id<GREYMatcher>)ntpFeedMenuEnableButton;
-
-// Returns matcher for the NTP Feed menu button which disables the feed.
-+ (id<GREYMatcher>)ntpFeedMenuDisableButton;
-
 // Returns matcher for the warning message while filling in payment requests.
 + (id<GREYMatcher>)warningMessageView;
 
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index f574939..550913a93 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -369,13 +369,6 @@
                     grey_sufficientlyVisible(), nil);
 }
 
-+ (id<GREYMatcher>)openNewTabButton {
-  return grey_allOf(
-      [ChromeMatchersAppInterface
-          buttonWithAccessibilityLabelID:(IDS_IOS_TOOLS_MENU_NEW_TAB)],
-      grey_sufficientlyVisible(), nil);
-}
-
 + (id<GREYMatcher>)shareButton {
   return grey_allOf(
       [ChromeMatchersAppInterface
@@ -721,16 +714,6 @@
   return grey_accessibilityID(kNTPIncognitoViewIdentifier);
 }
 
-+ (id<GREYMatcher>)ntpFeedMenuEnableButton {
-  return [ChromeMatchersAppInterface
-      buttonWithAccessibilityLabelID:IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM];
-}
-
-+ (id<GREYMatcher>)ntpFeedMenuDisableButton {
-  return [ChromeMatchersAppInterface
-      buttonWithAccessibilityLabelID:IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM];
-}
-
 // TODO(crbug.com/1021752): Remove this stub.
 + (id<GREYMatcher>)warningMessageView {
   return nil;
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 9202770..833da0a6 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -46,6 +46,7 @@
       "ipc_channel_proxy.h",
       "ipc_channel_reader.cc",
       "ipc_channel_reader.h",
+      "ipc_listener.cc",
       "ipc_listener.h",
       "ipc_logging.cc",
       "ipc_logging.h",
diff --git a/ipc/ipc_listener.cc b/ipc/ipc_listener.cc
new file mode 100644
index 0000000..e50a3fee
--- /dev/null
+++ b/ipc/ipc_listener.cc
@@ -0,0 +1,13 @@
+// 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 "ipc/ipc_listener.h"
+
+namespace IPC {
+
+std::string Listener::ToDebugString() {
+  return "IPC::Listener";
+}
+
+}  // namespace IPC
diff --git a/ipc/ipc_listener.h b/ipc/ipc_listener.h
index 398bd41..426bc32 100644
--- a/ipc/ipc_listener.h
+++ b/ipc/ipc_listener.h
@@ -51,6 +51,10 @@
   virtual void OnChannelListenError() {}
 #endif  // OS_POSIX || OS_FUCHSIA
 
+  // Debugging helper for identifying what kind of a Listener this is.
+  // TODO(https://crbug.com/1113159): Remove this method once the bug is fixed.
+  virtual std::string ToDebugString();
+
  protected:
   virtual ~Listener() {}
 };
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index 81667e0b..d24a03d 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -186,6 +186,8 @@
   sources += [
     "hls/items.cc",
     "hls/items.h",
+    "hls/parse_context.cc",
+    "hls/parse_context.h",
     "hls/parse_status.h",
     "hls/types.cc",
     "hls/types.h",
diff --git a/media/formats/hls/items.cc b/media/formats/hls/items.cc
index 97c41c5..32e150a 100644
--- a/media/formats/hls/items.cc
+++ b/media/formats/hls/items.cc
@@ -17,51 +17,16 @@
 
 namespace {
 
-// A non-blank line. This may be a comment, tag, or URI.
-struct LineItem {
-  size_t number;
-
-  // The content of the line. This does not include the line ending.
-  base::StringPiece text;
-};
-
-// Constructs a new Line from the given source text. Verifies that line endings
-// are respected, and advances `src` and `line_number` to the following line.
-ParseStatus::Or<LineItem> GetLineItem(base::StringPiece* src,
-                                      size_t* line_number) {
-  // Caller must not pass in an empty line
-  DCHECK(!src->empty());
-
-  auto line_end = src->find_first_of("\r\n");
-  if (line_end == base::StringPiece::npos) {
-    return ParseStatusCode::kInvalidEOL;
-  }
-
-  auto text = src->substr(0, line_end);
-  const auto following = src->substr(line_end);
-
-  // Trim (and validate) newline sequence from the following text
-  if (base::StartsWith(following, "\n")) {
-    *src = following.substr(1);
-  } else if (base::StartsWith(following, "\r\n")) {
-    *src = following.substr(2);
-  } else {
-    return ParseStatusCode::kInvalidEOL;
-  }
-
-  return LineItem{.number = (*line_number)++, .text = text};
-}
-
 // Attempts to determine tag type, if this line contains a tag.
-absl::optional<TagItem> GetTagItem(LineItem line) {
+absl::optional<TagItem> GetTagItem(SourceString line) {
   constexpr base::StringPiece kTagPrefix = "#EXT";
 
   // If this line does not begin with #EXT prefix, it must be a comment.
-  if (!base::StartsWith(line.text, kTagPrefix)) {
+  if (!base::StartsWith(line.Str(), kTagPrefix)) {
     return absl::nullopt;
   }
 
-  auto content = line.text.substr(kTagPrefix.size());
+  auto content = line.Substr(kTagPrefix.size());
 
   constexpr std::pair<base::StringPiece, TagKind> kTagKindPrefixes[] = {
       {"M3U", TagKind::kM3u},
@@ -70,19 +35,19 @@
   };
 
   for (const auto& tag : kTagKindPrefixes) {
-    if (base::StartsWith(content, tag.first)) {
-      content = content.substr(tag.first.size());
-      return TagItem(tag.second, line.number, content);
+    if (base::StartsWith(content.Str(), tag.first)) {
+      content = content.Substr(tag.first.size());
+      return TagItem(tag.second, content);
     }
   }
 
-  return TagItem(TagKind::kUnknown, line.number, content);
+  return TagItem(TagKind::kUnknown, content);
 }
 
 }  // namespace
 
-TagItem::TagItem(TagKind kind, size_t line_number, base::StringPiece content)
-    : kind(kind), line_number(line_number), content(content) {}
+TagItem::TagItem(TagKind kind, SourceString content)
+    : kind(kind), content(content) {}
 
 TagItem::~TagItem() = default;
 TagItem::TagItem(const TagItem&) = default;
@@ -90,8 +55,7 @@
 TagItem& TagItem::operator=(const TagItem&) = default;
 TagItem& TagItem::operator=(TagItem&&) = default;
 
-UriItem::UriItem(size_t line_number, base::StringPiece text)
-    : line_number(line_number), text(text) {}
+UriItem::UriItem(SourceString content) : content(content) {}
 
 UriItem::~UriItem() = default;
 UriItem::UriItem(const UriItem&) = default;
@@ -99,10 +63,10 @@
 UriItem& UriItem::operator=(const UriItem&) = default;
 UriItem& UriItem::operator=(UriItem&&) = default;
 
-ParseStatus::Or<GetNextLineItemResult> GetNextLineItem(base::StringPiece* src,
-                                                       size_t* line_number) {
-  while (!src->empty()) {
-    auto result = GetLineItem(src, line_number);
+ParseStatus::Or<GetNextLineItemResult> GetNextLineItem(
+    SourceLineIterator* src) {
+  while (true) {
+    auto result = src->Next();
     if (result.has_error()) {
       // Forward error to caller
       return std::move(result).error();
@@ -111,26 +75,25 @@
     auto line = std::move(result).value();
 
     // Empty lines are permitted, but ignored
-    if (line.text.empty()) {
+    if (line.Str().empty()) {
       continue;
     }
 
     // Tags and comments start with '#', try to get a tag
-    if (line.text.front() == '#') {
+    if (line.Str().front() == '#') {
       auto tag = GetTagItem(line);
       if (!tag.has_value()) {
         continue;
       }
-      return GetNextLineItemResult(std::move(tag).value());
+
+      return GetNextLineItemResult{std::move(tag).value()};
     }
 
     // If not empty, tag, or comment, it must be a URI.
     // This line may contain leading, trailing, or interior whitespace,
     // but that's the URI parser's responsibility.
-    return GetNextLineItemResult(UriItem(line.number, line.text));
+    return GetNextLineItemResult{UriItem(line)};
   }
-
-  return ParseStatusCode::kReachedEOF;
 }
 
 }  // namespace hls
diff --git a/media/formats/hls/items.h b/media/formats/hls/items.h
index 4b59c31..ed8f0db 100644
--- a/media/formats/hls/items.h
+++ b/media/formats/hls/items.h
@@ -6,8 +6,8 @@
 #define MEDIA_FORMATS_HLS_ITEMS_H_
 
 #include <cstddef>
-#include "base/strings/string_piece.h"
 #include "media/base/media_export.h"
+#include "media/formats/hls/parse_context.h"
 #include "media/formats/hls/parse_status.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
@@ -30,7 +30,7 @@
 // a comment.
 struct MEDIA_EXPORT TagItem {
   // TODO(crbug.com/1275317): These constructors should be removed
-  TagItem(TagKind, size_t line_number, base::StringPiece content);
+  TagItem(TagKind, SourceString content);
   ~TagItem();
   TagItem(const TagItem&);
   TagItem(TagItem&&);
@@ -38,36 +38,30 @@
   TagItem& operator=(TagItem&&);
 
   TagKind kind;
-  size_t line_number;
 
-  // The content of the tag. This does not include the tag prefix or line
-  // ending.
-  base::StringPiece content;
+  // The content of the tag, not including the tag type prefix.
+  SourceString content;
 };
 
 // A URI. This may be a URI line or a URI appearing within a tag.
 struct MEDIA_EXPORT UriItem {
   // TODO(crbug.com/1275317): These constructors should be removed
-  UriItem(size_t line_number, base::StringPiece text);
+  explicit UriItem(SourceString content);
   ~UriItem();
   UriItem(const UriItem&);
   UriItem(UriItem&&);
   UriItem& operator=(const UriItem&);
   UriItem& operator=(UriItem&&);
 
-  size_t line_number;
-  base::StringPiece text;
+  SourceString content;
 };
 
 using GetNextLineItemResult = absl::variant<TagItem, UriItem>;
 
-// Returns the next line-level item from the source text. Verifies that line
-// endings are respected, and advances `src` and `line_number` to the following
-// line. If no further items could be retrieved, returns
-// `ParseStatusCode::kReachedEOF`.
+// Returns the next line-level item from the source text. Automatically skips
+// empty lines.
 MEDIA_EXPORT ParseStatus::Or<GetNextLineItemResult> GetNextLineItem(
-    base::StringPiece* src,
-    size_t* line_number);
+    SourceLineIterator* src);
 
 }  // namespace hls
 }  // namespace media
diff --git a/media/formats/hls/items_fuzzer.cc b/media/formats/hls/items_fuzzer.cc
index d65340cbc..c7a88c9 100644
--- a/media/formats/hls/items_fuzzer.cc
+++ b/media/formats/hls/items_fuzzer.cc
@@ -17,59 +17,62 @@
          base.data() + base.size() >= sub.data() + sub.size();
 }
 
+media::hls::SourceString GetItemContent(media::hls::TagItem tag) {
+  // Ensure the tag kind returned was valid
+  CHECK(tag.kind >= media::hls::TagKind::kUnknown &&
+        tag.kind <= media::hls::TagKind::kMaxValue);
+
+  return tag.content;
+}
+
+media::hls::SourceString GetItemContent(media::hls::UriItem uri) {
+  return uri.content;
+}
+
 }  // namespace
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   // Create a StringPiece from the given input
-  base::StringPiece manifest(reinterpret_cast<const char*>(data), size);
-  size_t line_number = 1;
+  const base::StringPiece manifest(reinterpret_cast<const char*>(data), size);
+  media::hls::SourceLineIterator iterator{manifest};
 
   while (true) {
-    const auto old_manifest = manifest;
-    const auto old_line_number = line_number;
-    auto result = media::hls::GetNextLineItem(&manifest, &line_number);
+    const auto prev_iterator = iterator;
+    auto result = media::hls::GetNextLineItem(&iterator);
 
     if (result.has_error()) {
       // Ensure that this was an error this function is expected to return
       CHECK(result.code() == media::hls::ParseStatusCode::kReachedEOF ||
             result.code() == media::hls::ParseStatusCode::kInvalidEOL);
 
-      // Ensure that `manifest` is still a substring of the original manifest
-      CHECK(IsSubstring(manifest, old_manifest));
+      // Ensure that `manifest` is still a substring of the previous manifest
+      CHECK(IsSubstring(iterator.SourceForTesting(),
+                        prev_iterator.SourceForTesting()));
+      CHECK(iterator.CurrentLineForTesting() >=
+            prev_iterator.CurrentLineForTesting());
       break;
     }
 
     auto value = std::move(result).value();
-    base::StringPiece item_content;
-    size_t item_line_number;
-    static_assert(
-        absl::variant_size<media::hls::GetNextLineItemResult>::value == 2, "");
-    if (auto* tag = absl::get_if<media::hls::TagItem>(&value)) {
-      item_content = tag->content;
-      item_line_number = tag->line_number;
-
-      // Ensure the tag kind returned was valid
-      CHECK(tag->kind >= media::hls::TagKind::kUnknown &&
-            tag->kind <= media::hls::TagKind::kMaxValue);
-    } else {
-      auto uri = absl::get<media::hls::UriItem>(std::move(value));
-      item_content = uri.text;
-      item_line_number = uri.line_number;
-    }
+    auto content = absl::visit([](auto x) { return GetItemContent(x); }, value);
 
     // Ensure that the line number associated with this item is between the
     // original line number and the updated line number
-    CHECK(item_line_number >= old_line_number &&
-          item_line_number < line_number);
+    CHECK(content.Line() >= prev_iterator.CurrentLineForTesting() &&
+          content.Line() < iterator.CurrentLineForTesting());
 
-    // Ensure that `item_content` is a substring of the original manifest
-    CHECK(IsSubstring(item_content, old_manifest));
+    // Ensure that the content associated with this item is a substring of the
+    // previous iterator
+    CHECK(IsSubstring(content.Str(), prev_iterator.SourceForTesting()));
 
-    // Ensure that `manifest` is a substring of the original manifest
-    CHECK(IsSubstring(manifest, old_manifest));
+    // Ensure that the updated iterator contains a substring of the previous
+    // iterator
+    CHECK(IsSubstring(iterator.SourceForTesting(),
+                      prev_iterator.SourceForTesting()));
 
-    // Ensure that `item_content` is NOT a substring of the updated manifest
-    CHECK(!IsSubstring(item_content, manifest));
+    // Ensure that the content associated with this item is NOT a substring of
+    // the updated iterator
+    CHECK(!IsSubstring(content.Str(), iterator.SourceForTesting()));
   }
 
   return 0;
diff --git a/media/formats/hls/items_unittest.cc b/media/formats/hls/items_unittest.cc
index efb11db3..2693c5e 100644
--- a/media/formats/hls/items_unittest.cc
+++ b/media/formats/hls/items_unittest.cc
@@ -14,14 +14,20 @@
 
 using LineResult = GetNextLineItemResult;
 
+void CheckSourceString(SourceString expected, SourceString actual) {
+  EXPECT_EQ(expected.Line(), actual.Line());
+  EXPECT_EQ(expected.Column(), actual.Column());
+  EXPECT_EQ(expected.Str(), actual.Str());
+}
+
 // Calls `GetNextLineItem` for each expectation, and verifies that the result
 // matches.
 template <typename T>
 void RunTest(base::StringPiece manifest, const T& expectations) {
-  size_t line_number = 1;
+  auto line_iter = SourceLineIterator(manifest);
 
   for (auto expectation : expectations) {
-    auto result = GetNextLineItem(&manifest, &line_number);
+    auto result = GetNextLineItem(&line_iter);
 
     if (expectation.has_value()) {
       auto expected_value = std::move(expectation).value();
@@ -34,13 +40,11 @@
       if (auto* expected_tag = absl::get_if<TagItem>(&expected_value)) {
         auto tag = absl::get<TagItem>(std::move(value));
         EXPECT_EQ(expected_tag->kind, tag.kind);
-        EXPECT_EQ(expected_tag->line_number, tag.line_number);
-        EXPECT_EQ(expected_tag->content, tag.content);
+        CheckSourceString(expected_tag->content, tag.content);
       } else {
         auto expected_uri = absl::get<UriItem>(std::move(expected_value));
         auto uri = absl::get<UriItem>(std::move(value));
-        EXPECT_EQ(expected_uri.line_number, uri.line_number);
-        EXPECT_EQ(expected_uri.text, uri.text);
+        CheckSourceString(expected_uri.content, uri.content);
       }
     } else {
       EXPECT_TRUE(result.has_error());
@@ -51,6 +55,21 @@
   }
 }
 
+ParseStatus::Or<LineResult> ExpectTag(TagKind kind,
+                                      size_t line,
+                                      size_t col,
+                                      base::StringPiece content) {
+  return LineResult(
+      TagItem(kind, SourceString::CreateForTesting(line, col, content)));
+}
+
+ParseStatus::Or<LineResult> ExpectUri(size_t line,
+                                      size_t col,
+                                      base::StringPiece content) {
+  return LineResult(
+      UriItem(SourceString::CreateForTesting(line, col, content)));
+}
+
 }  // namespace
 
 TEST(HlsFormatParserTest, GetNextLineItemTest1) {
@@ -74,23 +93,25 @@
       "#EXTINF:1234,\t\n";
 
   const ParseStatus::Or<LineResult> kExpectations[] = {
-      LineResult(TagItem(TagKind::kM3u, 1, "")),
+      ExpectTag(TagKind::kM3u, 1, 8, ""),
       // Unknown tag content should be entire line following "#EXT"
-      LineResult(TagItem(TagKind::kUnknown, 5, "asdf")),
+      ExpectTag(TagKind::kUnknown, 5, 5, "asdf"),
+
       // Lines without leading # should be considered URIs
-      LineResult(UriItem(7, "EXTM3U")),
-      LineResult(UriItem(10, "http://www.example.com")),
-      LineResult(UriItem(11, "../media.m3u8")),
-      LineResult(UriItem(12, "foobar.jpg")),
+      ExpectUri(7, 1, "EXTM3U"), ExpectUri(10, 1, "http://www.example.com"),
+      ExpectUri(11, 1, "../media.m3u8"), ExpectUri(12, 1, "foobar.jpg"),
+
       // Whitespace is not allowed here, but that's not this function's
       // responsibility.
-      LineResult(UriItem(13, " video about food.mov")),
+      ExpectUri(13, 1, " video about food.mov"),
+
       // Variable substitution is not this function's responsibility.
-      LineResult(UriItem(14, "uri_with_{$variable}.mov")),
-      LineResult(TagItem(TagKind::kXVersion, 15, "7")),
-      LineResult(TagItem(TagKind::kXVersion, 16, "")),
-      LineResult(TagItem(TagKind::kInf, 17, "1234,\t")),
-      ParseStatusCode::kReachedEOF, ParseStatusCode::kReachedEOF};
+      ExpectUri(14, 1, "uri_with_{$variable}.mov"),
+
+      ExpectTag(TagKind::kXVersion, 15, 16, "7"),
+      ExpectTag(TagKind::kXVersion, 16, 16, ""),
+      ExpectTag(TagKind::kInf, 17, 9, "1234,\t"), ParseStatusCode::kReachedEOF,
+      ParseStatusCode::kReachedEOF};
 
   RunTest(kManifest, kExpectations);
 }
@@ -102,7 +123,7 @@
       "#EXT-X-VERSION:3\n";
 
   const ParseStatus::Or<LineResult> kExpectations[] = {
-      LineResult(TagItem(TagKind::kM3u, 1, "")), ParseStatusCode::kInvalidEOL,
+      ExpectTag(TagKind::kM3u, 1, 8, ""), ParseStatusCode::kInvalidEOL,
       ParseStatusCode::kInvalidEOL};
 
   RunTest(kManifest, kExpectations);
diff --git a/media/formats/hls/parse_context.cc b/media/formats/hls/parse_context.cc
new file mode 100644
index 0000000..2905749
--- /dev/null
+++ b/media/formats/hls/parse_context.cc
@@ -0,0 +1,81 @@
+// 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 "media/formats/hls/parse_context.h"
+
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/types/pass_key.h"
+#include "media/formats/hls/parse_status.h"
+
+namespace media {
+namespace hls {
+
+SourceString SourceString::Create(base::PassKey<SourceLineIterator>,
+                                  size_t line,
+                                  base::StringPiece str) {
+  return SourceString(line, 1, str);
+}
+
+SourceString SourceString::CreateForTesting(size_t line,
+                                            size_t column,
+                                            base::StringPiece str) {
+  return SourceString(line, column, str);
+}
+
+SourceString::SourceString(size_t line, size_t column, base::StringPiece str)
+    : line_(line), column_(column), str_(str) {}
+
+SourceString::~SourceString() = default;
+SourceString::SourceString(const SourceString&) = default;
+SourceString::SourceString(SourceString&&) = default;
+SourceString& SourceString::operator=(const SourceString&) = default;
+SourceString& SourceString::operator=(SourceString&&) = default;
+
+SourceString SourceString::Substr(size_t pos, size_t count) const {
+  const auto column = column_ + pos;
+  return SourceString(line_, column, str_.substr(pos, count));
+}
+
+SourceLineIterator::SourceLineIterator(base::StringPiece source)
+    : current_line_(1), source_(source) {}
+
+SourceLineIterator::~SourceLineIterator() = default;
+SourceLineIterator::SourceLineIterator(const SourceLineIterator&) = default;
+SourceLineIterator::SourceLineIterator(SourceLineIterator&&) = default;
+SourceLineIterator& SourceLineIterator::operator=(const SourceLineIterator&) =
+    default;
+SourceLineIterator& SourceLineIterator::operator=(SourceLineIterator&&) =
+    default;
+
+ParseStatus::Or<SourceString> SourceLineIterator::Next() {
+  if (source_.empty()) {
+    return ParseStatusCode::kReachedEOF;
+  }
+
+  const auto line_end = source_.find_first_of("\r\n");
+  if (line_end == base::StringPiece::npos) {
+    return ParseStatusCode::kInvalidEOL;
+  }
+
+  const auto line_content = source_.substr(0, line_end);
+  const auto following = source_.substr(line_end);
+
+  // Trim (and validate) newline sequence from the following text
+  if (base::StartsWith(following, "\n")) {
+    source_ = following.substr(1);
+  } else if (base::StartsWith(following, "\r\n")) {
+    source_ = following.substr(2);
+  } else {
+    return ParseStatusCode::kInvalidEOL;
+  }
+
+  const auto line_number = current_line_;
+  current_line_ += 1;
+
+  return SourceString::Create({}, line_number, line_content);
+}
+
+}  // namespace hls
+}  // namespace media
diff --git a/media/formats/hls/parse_context.h b/media/formats/hls/parse_context.h
new file mode 100644
index 0000000..53ad33e
--- /dev/null
+++ b/media/formats/hls/parse_context.h
@@ -0,0 +1,90 @@
+// 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.
+
+#ifndef MEDIA_FORMATS_HLS_PARSE_CONTEXT_H_
+#define MEDIA_FORMATS_HLS_PARSE_CONTEXT_H_
+
+#include <cstdint>
+#include "base/strings/string_piece.h"
+#include "base/types/pass_key.h"
+#include "media/base/media_export.h"
+#include "media/formats/hls/parse_status.h"
+
+namespace media {
+namespace hls {
+
+struct SourceLineIterator;
+
+// This structure represents contents of a single line in an HLS manifest, not
+// including the line ending. This may be the entire line, or a substring of the
+// line (clipped at either/both ends).
+struct MEDIA_EXPORT SourceString {
+  static SourceString Create(base::PassKey<SourceLineIterator>,
+                             size_t line,
+                             base::StringPiece str);
+  static SourceString CreateForTesting(size_t line,
+                                       size_t column,
+                                       base::StringPiece str);
+
+  // TODO(crbug.com/1275317): These should be removed
+  ~SourceString();
+  SourceString(const SourceString&);
+  SourceString(SourceString&&);
+  SourceString& operator=(const SourceString&);
+  SourceString& operator=(SourceString&&);
+
+  // Returns the 1-based line index of this SourceString within the manifest.
+  size_t Line() const { return line_; }
+
+  // Returns the 1-based index of the first character of this SourceString from
+  // the start of the line within the manifest.
+  size_t Column() const { return column_; }
+
+  // Returns the contents of this SourceString. This will never include line-end
+  // characters.
+  base::StringPiece Str() const { return str_; }
+
+  SourceString Substr(size_t pos = 0,
+                      size_t count = base::StringPiece::npos) const;
+
+ private:
+  SourceString(size_t line, size_t column, base::StringPiece str);
+
+  size_t line_;
+  size_t column_;
+  base::StringPiece str_;
+};
+
+// Exposes a line-based iteration API over the source text of an HLS manifest.
+struct MEDIA_EXPORT SourceLineIterator {
+  explicit SourceLineIterator(base::StringPiece source);
+
+  // TODO(crbug.com/1275317): These should be removed
+  ~SourceLineIterator();
+  SourceLineIterator(const SourceLineIterator&);
+  SourceLineIterator(SourceLineIterator&&);
+  SourceLineIterator& operator=(const SourceLineIterator&);
+  SourceLineIterator& operator=(SourceLineIterator&&);
+
+  // Moves this SourceLineIterator to the next line, and returns the contents of
+  // the current line. Returns `ParseStatusCode::kInvalidEOL` if invalid line
+  // endings were found, or `ParseStatusCode::kReachedEOF` if no further lines
+  // exist in the manifest.
+  ParseStatus::Or<SourceString> Next();
+
+  size_t CurrentLineForTesting() const { return current_line_; }
+  base::StringPiece SourceForTesting() const { return source_; }
+
+ private:
+  size_t current_line_;
+  base::StringPiece source_;
+};
+
+// TODO(https://crbug.com/1266991): Add `MediaPlaylistParseContext`,
+// `MultivariantPlaylistParseContext`, and `VariableDictionary` structures.
+
+}  // namespace hls
+}  // namespace media
+
+#endif  // MEDIA_FORMATS_HLS_PARSE_CONTEXT_H_
diff --git a/media/formats/hls/types.cc b/media/formats/hls/types.cc
index cea23fb1..48414d70a 100644
--- a/media/formats/hls/types.cc
+++ b/media/formats/hls/types.cc
@@ -8,15 +8,18 @@
 
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "media/formats/hls/parse_context.h"
 #include "third_party/re2/src/re2/re2.h"
 
 namespace media {
 namespace hls {
 namespace types {
 
-ParseStatus::Or<DecimalInteger> ParseDecimalInteger(base::StringPiece str) {
+ParseStatus::Or<DecimalInteger> ParseDecimalInteger(SourceString source_str) {
   static const base::NoDestructor<re2::RE2> decimal_integer_regex("\\d{1,20}");
 
+  const auto str = source_str.Str();
+
   // Check that the set of characters is allowed: 0-9
   // NOTE: It may be useful to split this into a separate function which
   // extracts the range containing valid characters from a given
@@ -35,9 +38,9 @@
 }
 
 ParseStatus::Or<DecimalFloatingPoint> ParseDecimalFloatingPoint(
-    base::StringPiece str) {
+    SourceString source_str) {
   // Utilize signed parsing function
-  auto result = ParseSignedDecimalFloatingPoint(str);
+  auto result = ParseSignedDecimalFloatingPoint(source_str);
   if (result.has_error()) {
     return ParseStatusCode::kFailedToParseDecimalFloatingPoint;
   }
@@ -52,12 +55,14 @@
 }
 
 ParseStatus::Or<SignedDecimalFloatingPoint> ParseSignedDecimalFloatingPoint(
-    base::StringPiece str) {
+    SourceString source_str) {
   // Accept no decimal point, decimal point with leading digits, trailing
   // digits, or both
   static const base::NoDestructor<re2::RE2> decimal_floating_point_regex(
       "-?(\\d+|\\d+\\.|\\.\\d+|\\d+\\.\\d+)");
 
+  const auto str = source_str.Str();
+
   // Check that the set of characters is allowed: - . 0-9
   // `base::StringToDouble` is not as strict as the HLS spec
   if (!re2::RE2::FullMatch(re2::StringPiece(str.data(), str.size()),
diff --git a/media/formats/hls/types.h b/media/formats/hls/types.h
index 7224b55..f815267 100644
--- a/media/formats/hls/types.h
+++ b/media/formats/hls/types.h
@@ -6,11 +6,13 @@
 #define MEDIA_FORMATS_HLS_TYPES_H_
 
 #include <cstdint>
-#include "base/strings/string_piece.h"
 #include "media/formats/hls/parse_status.h"
 
 namespace media {
 namespace hls {
+
+struct SourceString;
+
 namespace types {
 
 // Data-types used in HLS, as described by the spec
@@ -19,13 +21,13 @@
 using SignedDecimalFloatingPoint = double;
 
 ParseStatus::Or<DecimalInteger> MEDIA_EXPORT
-ParseDecimalInteger(base::StringPiece str);
+ParseDecimalInteger(SourceString source_str);
 
 ParseStatus::Or<DecimalFloatingPoint> MEDIA_EXPORT
-ParseDecimalFloatingPoint(base::StringPiece str);
+ParseDecimalFloatingPoint(SourceString source_str);
 
 ParseStatus::Or<SignedDecimalFloatingPoint> MEDIA_EXPORT
-ParseSignedDecimalFloatingPoint(base::StringPiece str);
+ParseSignedDecimalFloatingPoint(SourceString source_str);
 
 }  // namespace types
 }  // namespace hls
diff --git a/media/formats/hls/types_unittest.cc b/media/formats/hls/types_unittest.cc
index b160439..b4b9cf2b 100644
--- a/media/formats/hls/types_unittest.cc
+++ b/media/formats/hls/types_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "media/formats/hls/types.h"
+#include "media/formats/hls/parse_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -10,7 +11,8 @@
 
 TEST(HlsFormatParserTest, ParseDecimalIntegerTest) {
   auto const error_test = [](base::StringPiece input) {
-    auto result = types::ParseDecimalInteger(input);
+    auto result =
+        types::ParseDecimalInteger(SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_error());
     auto error = std::move(result).error();
     EXPECT_EQ(error.code(), ParseStatusCode::kFailedToParseDecimalInteger);
@@ -18,7 +20,8 @@
 
   auto const ok_test = [](base::StringPiece input,
                           types::DecimalInteger expected) {
-    auto result = types::ParseDecimalInteger(input);
+    auto result =
+        types::ParseDecimalInteger(SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_value());
     auto value = std::move(result).value();
     EXPECT_EQ(value, expected);
@@ -58,7 +61,8 @@
 
 TEST(HlsFormatParserTest, ParseDecimalFloatingPointTest) {
   auto const error_test = [](base::StringPiece input) {
-    auto result = types::ParseDecimalFloatingPoint(input);
+    auto result = types::ParseDecimalFloatingPoint(
+        SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_error());
     auto error = std::move(result).error();
     EXPECT_EQ(error.code(),
@@ -67,7 +71,8 @@
 
   auto const ok_test = [](base::StringPiece input,
                           types::DecimalFloatingPoint expected) {
-    auto result = types::ParseDecimalFloatingPoint(input);
+    auto result = types::ParseDecimalFloatingPoint(
+        SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_value());
     auto value = std::move(result).value();
     EXPECT_DOUBLE_EQ(value, expected);
@@ -104,7 +109,8 @@
 
 TEST(HlsFormatParserTest, ParseSignedDecimalFloatingPointTest) {
   auto const error_test = [](base::StringPiece input) {
-    auto result = types::ParseSignedDecimalFloatingPoint(input);
+    auto result = types::ParseSignedDecimalFloatingPoint(
+        SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_error());
     auto error = std::move(result).error();
     EXPECT_EQ(error.code(),
@@ -113,7 +119,8 @@
 
   auto const ok_test = [](base::StringPiece input,
                           types::SignedDecimalFloatingPoint expected) {
-    auto result = types::ParseSignedDecimalFloatingPoint(input);
+    auto result = types::ParseSignedDecimalFloatingPoint(
+        SourceString::CreateForTesting(1, 1, input));
     EXPECT_TRUE(result.has_value());
     auto value = std::move(result).value();
     EXPECT_DOUBLE_EQ(value, expected);
diff --git a/net/BUILD.gn b/net/BUILD.gn
index c5b20b3..ac7e806 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1158,6 +1158,8 @@
       "android/network_change_notifier_factory_android.h",
       "android/network_library.cc",
       "android/network_library.h",
+      "android/radio_activity_tracker.cc",
+      "android/radio_activity_tracker.h",
       "android/traffic_stats.cc",
       "android/traffic_stats.h",
       "cert/cert_verify_proc_android.cc",
diff --git a/net/android/radio_activity_tracker.cc b/net/android/radio_activity_tracker.cc
new file mode 100644
index 0000000..4c23e01
--- /dev/null
+++ b/net/android/radio_activity_tracker.cc
@@ -0,0 +1,89 @@
+// 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 "net/android/radio_activity_tracker.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "net/base/features.h"
+
+namespace net {
+namespace android {
+
+namespace {
+
+// The minimum interval for recording possible radio wake-ups. It's unlikely
+// that radio state transitions happen in seconds.
+constexpr base::TimeDelta kMinimumRecordIntervalForPossibleWakeupTrigger =
+    base::Seconds(1);
+
+}  // namespace
+
+// static
+RadioActivityTracker& RadioActivityTracker::GetInstance() {
+  static base::NoDestructor<RadioActivityTracker> s_instance;
+  return *s_instance;
+}
+
+RadioActivityTracker::RadioActivityTracker() = default;
+
+bool RadioActivityTracker::ShouldRecordActivityForWakeupTrigger() {
+  if (!base::FeatureList::IsEnabled(features::kRecordRadioWakeupTrigger))
+    return false;
+
+  if (!IsRadioUtilsSupported())
+    return false;
+
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  // Check recording interval first to reduce overheads of calling Android's
+  // platform APIs.
+  if (!last_check_time_.is_null() &&
+      now - last_check_time_ < kMinimumRecordIntervalForPossibleWakeupTrigger)
+    return false;
+
+  last_check_time_ = now;
+
+  bool should_record = ShouldRecordActivityForWakeupTriggerInternal();
+
+  // TODO(crbug.com/1232623): Use "Net." prefix instead of "Network."
+  base::UmaHistogramTimes(
+      "Network.Radio.PossibleWakeupTrigger.RadioUtilsOverhead",
+      base::TimeTicks::Now() - now);
+
+  return should_record;
+}
+
+bool RadioActivityTracker::IsRadioUtilsSupported() {
+  return base::android::RadioUtils::IsSupported() ||
+         radio_activity_override_for_testing_.has_value() ||
+         radio_type_override_for_testing_.has_value();
+}
+
+bool RadioActivityTracker::ShouldRecordActivityForWakeupTriggerInternal() {
+  base::android::RadioConnectionType radio_type =
+      radio_type_override_for_testing_.value_or(
+          base::android::RadioUtils::GetConnectionType());
+  if (radio_type != base::android::RadioConnectionType::kCell)
+    return false;
+
+  absl::optional<base::android::RadioDataActivity> radio_activity =
+      radio_activity_override_for_testing_.has_value()
+          ? radio_activity_override_for_testing_
+          : base::android::RadioUtils::GetCellDataActivity();
+
+  if (!radio_activity.has_value())
+    return false;
+
+  // When the last activity was dormant, don't treat this event as a wakeup
+  // trigger since there could be state transition delay and startup latency.
+  bool should_record =
+      *radio_activity == base::android::RadioDataActivity::kDormant &&
+      last_radio_data_activity_ != base::android::RadioDataActivity::kDormant;
+  last_radio_data_activity_ = *radio_activity;
+  return should_record;
+}
+
+}  // namespace android
+}  // namespace net
diff --git a/net/android/radio_activity_tracker.h b/net/android/radio_activity_tracker.h
new file mode 100644
index 0000000..21fcae2
--- /dev/null
+++ b/net/android/radio_activity_tracker.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef NET_ANDROID_RADIO_ACTIVITY_TRACKER_H_
+#define NET_ANDROID_RADIO_ACTIVITY_TRACKER_H_
+
+#include "base/android/radio_utils.h"
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+
+namespace android {
+
+// Tracks radio states and provides helper methods to record network activities
+// which may trigger power-consuming radio state changes like wake-ups.
+class NET_EXPORT RadioActivityTracker {
+ public:
+  static RadioActivityTracker& GetInstance();
+
+  RadioActivityTracker(const RadioActivityTracker&) = delete;
+  RadioActivityTracker& operator=(const RadioActivityTracker&) = delete;
+  RadioActivityTracker(RadioActivityTracker&&) = delete;
+  RadioActivityTracker& operator=(RadioActivityTracker&&) = delete;
+
+  // Returns true when a network activity such as creating a network request and
+  // resolving a host name could trigger a radio wakeup.
+  // TODO(crbug.com/1232623): Consider optimizing this function. This function
+  // uses Android's platform APIs which add non-negligible overheads.
+  bool ShouldRecordActivityForWakeupTrigger();
+
+  // These override internal members for testing.
+  void OverrideRadioActivityForTesting(
+      absl::optional<base::android::RadioDataActivity> radio_activity) {
+    radio_activity_override_for_testing_ = radio_activity;
+  }
+  void OverrideRadioTypeForTesting(
+      absl::optional<base::android::RadioConnectionType> radio_type) {
+    radio_type_override_for_testing_ = radio_type;
+  }
+  void OverrideLastCheckTimeForTesting(base::TimeTicks last_check_time) {
+    last_check_time_ = last_check_time;
+  }
+
+ private:
+  friend class base::NoDestructor<RadioActivityTracker>;
+  RadioActivityTracker();
+  ~RadioActivityTracker() = delete;
+
+  // Returns true when RadioUtils is available or any radio states are
+  // overridden for testing.
+  bool IsRadioUtilsSupported();
+
+  // Contains potentially expensive API calls. Factored out to measure
+  // overheads.
+  bool ShouldRecordActivityForWakeupTriggerInternal();
+
+  // Updated when ShouldRecordActivityForWakeupTrigger() is called.
+  base::android::RadioDataActivity last_radio_data_activity_ =
+      base::android::RadioDataActivity::kNone;
+  base::TimeTicks last_check_time_;
+
+  // Radio state overrides for testing.
+  absl::optional<base::android::RadioDataActivity>
+      radio_activity_override_for_testing_;
+  absl::optional<base::android::RadioConnectionType>
+      radio_type_override_for_testing_;
+};
+
+}  // namespace android
+}  // namespace net
+
+#endif  // NET_ANDROID_RADIO_ACTIVITY_TRACKER_H_
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index b10acec..14d716b 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/check_op.h"
-#include "base/memory/raw_ptr.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_piece.h"
 #include "base/time/tick_clock.h"
@@ -30,236 +29,6 @@
 
 namespace net {
 
-// Wrapper of ResolveHostRequests that on destruction will remove itself from
-// |ContextHostResolver::handed_out_requests_|.
-class ContextHostResolver::WrappedRequest {
- public:
-  WrappedRequest(ContextHostResolver* resolver, bool shutting_down)
-      : resolver_(resolver), shutting_down_(shutting_down) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(resolver_->sequence_checker_);
-  }
-
-  WrappedRequest(const WrappedRequest&) = delete;
-  WrappedRequest& operator=(const WrappedRequest&) = delete;
-
-  virtual ~WrappedRequest() { DetachFromResolver(); }
-
-  void Cancel() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    OnShutdown();
-    DetachFromResolver();
-  }
-
-  void OnShutdown() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    // Cannot destroy |inner_request_| because it is still allowed to call
-    // Get...Results() methods if the request was already complete.
-    if (inner_request())
-      inner_request()->Cancel();
-
-    shutting_down_ = true;
-
-    // Not clearing |resolver_| so that early shutdown can be differentiated in
-    // Start() from full cancellation on resolver destruction.
-  }
-
-  virtual HostResolverManager::CancellableRequest* inner_request() = 0;
-
-  ContextHostResolver* resolver() { return resolver_; }
-  bool shutting_down() { return shutting_down_; }
-
- private:
-  void DetachFromResolver() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (resolver_) {
-      DCHECK_EQ(1u, resolver_->handed_out_requests_.count(this));
-      resolver_->handed_out_requests_.erase(this);
-      resolver_ = nullptr;
-    }
-  }
-
-  // Resolver is expected to call Cancel() on destruction, clearing the pointer
-  // before it becomes invalid.
-  raw_ptr<ContextHostResolver> resolver_;
-  bool shutting_down_ = false;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-class ContextHostResolver::WrappedResolveHostRequest
-    : public WrappedRequest,
-      public HostResolver::ResolveHostRequest {
- public:
-  WrappedResolveHostRequest(
-      std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-          request,
-      ContextHostResolver* resolver,
-      bool shutting_down)
-      : WrappedRequest(resolver, shutting_down),
-        inner_request_(std::move(request)) {}
-
-  WrappedResolveHostRequest(const WrappedResolveHostRequest&) = delete;
-  WrappedResolveHostRequest& operator=(const WrappedResolveHostRequest&) =
-      delete;
-
-  int Start(CompletionOnceCallback callback) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    if (!resolver()) {
-      // Parent resolver has been destroyed. HostResolver generally disallows
-      // calling Start() in this case, but this implementation returns
-      // ERR_FAILED to allow testing the case.
-      inner_request_ = nullptr;
-      resolve_error_info_ = ResolveErrorInfo(ERR_FAILED);
-      return ERR_NAME_NOT_RESOLVED;
-    }
-
-    if (shutting_down()) {
-      // Shutting down but the resolver is not yet destroyed.
-      inner_request_ = nullptr;
-      resolve_error_info_ = ResolveErrorInfo(ERR_CONTEXT_SHUT_DOWN);
-      return ERR_NAME_NOT_RESOLVED;
-    }
-
-    DCHECK(inner_request_);
-    return inner_request_->Start(std::move(callback));
-  }
-
-  const absl::optional<AddressList>& GetAddressResults() const override {
-    if (!inner_request_) {
-      static base::NoDestructor<absl::optional<AddressList>> nullopt_result;
-      return *nullopt_result;
-    }
-
-    return inner_request_->GetAddressResults();
-  }
-
-  absl::optional<std::vector<HostResolverEndpointResult>> GetEndpointResults()
-      const override {
-    if (!inner_request_)
-      return absl::nullopt;
-
-    return inner_request_->GetEndpointResults();
-  }
-
-  const absl::optional<std::vector<std::string>>& GetTextResults()
-      const override {
-    if (!inner_request_) {
-      static const base::NoDestructor<absl::optional<std::vector<std::string>>>
-          nullopt_result;
-      return *nullopt_result;
-    }
-
-    return inner_request_->GetTextResults();
-  }
-
-  const absl::optional<std::vector<HostPortPair>>& GetHostnameResults()
-      const override {
-    if (!inner_request_) {
-      static const base::NoDestructor<absl::optional<std::vector<HostPortPair>>>
-          nullopt_result;
-      return *nullopt_result;
-    }
-
-    return inner_request_->GetHostnameResults();
-  }
-
-  const absl::optional<std::vector<std::string>>& GetDnsAliasResults()
-      const override {
-    if (!inner_request_) {
-      static const base::NoDestructor<absl::optional<std::vector<std::string>>>
-          nullopt_result;
-      return *nullopt_result;
-    }
-
-    return inner_request_->GetDnsAliasResults();
-  }
-
-  net::ResolveErrorInfo GetResolveErrorInfo() const override {
-    if (!inner_request_) {
-      return resolve_error_info_;
-    }
-    return inner_request_->GetResolveErrorInfo();
-  }
-
-  const absl::optional<HostCache::EntryStaleness>& GetStaleInfo()
-      const override {
-    if (!inner_request_) {
-      static const absl::optional<HostCache::EntryStaleness> nullopt_result;
-      return nullopt_result;
-    }
-
-    return inner_request_->GetStaleInfo();
-  }
-
-  void ChangeRequestPriority(RequestPriority priority) override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK(inner_request_);
-
-    inner_request_->ChangeRequestPriority(priority);
-  }
-
-  HostResolverManager::CancellableRequest* inner_request() override {
-    return inner_request_.get();
-  }
-
- private:
-  std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-      inner_request_;
-
-  // Error info for a |inner_request_| that was destroyed before it started.
-  ResolveErrorInfo resolve_error_info_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-class ContextHostResolver::WrappedProbeRequest
-    : public WrappedRequest,
-      public HostResolver::ProbeRequest {
- public:
-  WrappedProbeRequest(
-      std::unique_ptr<HostResolverManager::CancellableProbeRequest>
-          inner_request,
-      ContextHostResolver* resolver,
-      bool shutting_down)
-      : WrappedRequest(resolver, shutting_down),
-        inner_request_(std::move(inner_request)) {}
-
-  WrappedProbeRequest(const WrappedProbeRequest&) = delete;
-  WrappedProbeRequest& operator=(const WrappedProbeRequest&) = delete;
-
-  int Start() override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-    if (!resolver()) {
-      // Parent resolver has been destroyed. HostResolver generally disallows
-      // calling Start() in this case, but this implementation returns
-      // ERR_FAILED to allow testing the case.
-      inner_request_ = nullptr;
-      return ERR_FAILED;
-    }
-
-    if (shutting_down()) {
-      // Shutting down but the resolver is not yet destroyed.
-      inner_request_ = nullptr;
-      return ERR_CONTEXT_SHUT_DOWN;
-    }
-
-    DCHECK(inner_request_);
-    return inner_request_->Start();
-  }
-
-  HostResolverManager::CancellableRequest* inner_request() override {
-    return inner_request_.get();
-  }
-
- private:
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> inner_request_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
 ContextHostResolver::ContextHostResolver(
     HostResolverManager* manager,
     std::unique_ptr<ResolveContext> resolve_context)
@@ -278,24 +47,18 @@
 }
 
 ContextHostResolver::~ContextHostResolver() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (owned_manager_)
     DCHECK_EQ(owned_manager_.get(), manager_);
 
   // No |resolve_context_| to deregister if OnShutdown() was already called.
   if (resolve_context_)
     manager_->DeregisterResolveContext(resolve_context_.get());
-
-  // Silently cancel all requests associated with this resolver.
-  while (!handed_out_requests_.empty())
-    (*handed_out_requests_.begin())->Cancel();
 }
 
 void ContextHostResolver::OnShutdown() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  for (auto* active_request : handed_out_requests_)
-    active_request->OnShutdown();
-
   DCHECK(resolve_context_);
   manager_->DeregisterResolveContext(resolve_context_.get());
   resolve_context_.reset();
@@ -312,19 +75,13 @@
     absl::optional<ResolveHostParameters> optional_parameters) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-      inner_request;
-  if (!shutting_down_) {
-    inner_request = manager_->CreateRequest(
-        std::move(host), std::move(network_isolation_key),
-        std::move(source_net_log), std::move(optional_parameters),
-        resolve_context_.get(), resolve_context_->host_cache());
-  }
+  if (shutting_down_)
+    return HostResolver::CreateFailingRequest(ERR_CONTEXT_SHUT_DOWN);
 
-  auto request = std::make_unique<WrappedResolveHostRequest>(
-      std::move(inner_request), this, shutting_down_);
-  handed_out_requests_.insert(request.get());
-  return request;
+  return manager_->CreateRequest(
+      std::move(host), std::move(network_isolation_key),
+      std::move(source_net_log), std::move(optional_parameters),
+      resolve_context_.get(), resolve_context_->host_cache());
 }
 
 std::unique_ptr<HostResolver::ResolveHostRequest>
@@ -335,33 +92,22 @@
     const absl::optional<ResolveHostParameters>& optional_parameters) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-      inner_request;
-  if (!shutting_down_) {
-    inner_request = manager_->CreateRequest(
-        host, network_isolation_key, source_net_log, optional_parameters,
-        resolve_context_.get(), resolve_context_->host_cache());
-  }
+  if (shutting_down_)
+    return HostResolver::CreateFailingRequest(ERR_CONTEXT_SHUT_DOWN);
 
-  auto request = std::make_unique<WrappedResolveHostRequest>(
-      std::move(inner_request), this, shutting_down_);
-  handed_out_requests_.insert(request.get());
-  return request;
+  return manager_->CreateRequest(host, network_isolation_key, source_net_log,
+                                 optional_parameters, resolve_context_.get(),
+                                 resolve_context_->host_cache());
 }
 
 std::unique_ptr<HostResolver::ProbeRequest>
 ContextHostResolver::CreateDohProbeRequest() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> inner_request;
-  if (!shutting_down_) {
-    inner_request = manager_->CreateDohProbeRequest(resolve_context_.get());
-  }
+  if (shutting_down_)
+    return HostResolver::CreateFailingProbeRequest(ERR_CONTEXT_SHUT_DOWN);
 
-  auto request = std::make_unique<WrappedProbeRequest>(std::move(inner_request),
-                                                       this, shutting_down_);
-  handed_out_requests_.insert(request.get());
-  return request;
+  return manager_->CreateDohProbeRequest(resolve_context_.get());
 }
 
 std::unique_ptr<HostResolver::MdnsListener>
@@ -380,7 +126,6 @@
 
 void ContextHostResolver::SetRequestContext(
     URLRequestContext* request_context) {
-  DCHECK(handed_out_requests_.empty());
   DCHECK(!shutting_down_);
   DCHECK(resolve_context_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/net/dns/context_host_resolver.h b/net/dns/context_host_resolver.h
index 9ca0991..db88169 100644
--- a/net/dns/context_host_resolver.h
+++ b/net/dns/context_host_resolver.h
@@ -82,24 +82,9 @@
   void SetProcParamsForTesting(const ProcTaskParams& proc_params);
   void SetTickClockForTesting(const base::TickClock* tick_clock);
 
-  size_t GetNumActiveRequestsForTesting() const {
-    return handed_out_requests_.size();
-  }
-
  private:
-  class WrappedRequest;
-  class WrappedResolveHostRequest;
-  class WrappedProbeRequest;
-
   const raw_ptr<HostResolverManager> manager_;
   std::unique_ptr<HostResolverManager> owned_manager_;
-
-  // Requests are expected to clear themselves from this set on destruction or
-  // cancellation.  Requests in an early shutdown state (from
-  // HostResolver::OnShutdown()) are still in this set, so they can be notified
-  // on resolver destruction.
-  std::unordered_set<WrappedRequest*> handed_out_requests_;
-
   std::unique_ptr<ResolveContext> resolve_context_;
 
   // If true, the context is shutting down. Subsequent request Start() calls
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index e389f276..d13ac5ad 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -205,7 +205,6 @@
       resolver->CreateRequest(HostPortPair("example.com", 100),
                               NetworkIsolationKey(), NetLogWithSource(),
                               absl::nullopt);
-  EXPECT_EQ(1u, resolver->GetNumActiveRequestsForTesting());
 
   TestCompletionCallback callback;
   int rv = request->Start(callback.callback());
@@ -218,7 +217,6 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_THAT(rv, test::IsError(ERR_IO_PENDING));
   EXPECT_FALSE(callback.have_result());
-  EXPECT_EQ(0u, resolver->GetNumActiveRequestsForTesting());
 }
 
 TEST_F(ContextHostResolverTest, DohProbeRequest) {
@@ -373,28 +371,6 @@
               testing::ElementsAre(kEndpoint));
 }
 
-TEST_F(ContextHostResolverTest, DestroyResolver_DohProbeRequest) {
-  // Set empty MockDnsClient rules to ensure DnsClient is mocked out.
-  MockDnsClientRuleList rules;
-  SetMockDnsRules(std::move(rules));
-
-  URLRequestContext context;
-  auto resolve_context =
-      std::make_unique<ResolveContext>(&context, false /* enable_caching */);
-  auto resolver = std::make_unique<ContextHostResolver>(
-      manager_.get(), std::move(resolve_context));
-
-  std::unique_ptr<HostResolver::ProbeRequest> request =
-      resolver->CreateDohProbeRequest();
-
-  request->Start();
-  ASSERT_TRUE(dns_client_->factory()->doh_probes_running());
-
-  resolver.reset();
-
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-}
-
 // Test a request created before resolver destruction but not yet started.
 TEST_F(ContextHostResolverTest, DestroyResolver_DelayedStartRequest) {
   // Set up delayed result for "example.com".
@@ -423,7 +399,8 @@
   int rv = request->Start(callback.callback());
 
   EXPECT_THAT(callback.GetResult(rv), test::IsError(ERR_NAME_NOT_RESOLVED));
-  EXPECT_THAT(request->GetResolveErrorInfo().error, test::IsError(ERR_FAILED));
+  EXPECT_THAT(request->GetResolveErrorInfo().error,
+              test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_FALSE(request->GetAddressResults());
 }
 
@@ -443,7 +420,7 @@
 
   resolver = nullptr;
 
-  EXPECT_THAT(request->Start(), test::IsError(ERR_FAILED));
+  EXPECT_THAT(request->Start(), test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
 }
 
@@ -483,28 +460,6 @@
   EXPECT_FALSE(callback.have_result());
 }
 
-TEST_F(ContextHostResolverTest, OnShutdown_DohProbeRequest) {
-  // Set empty MockDnsClient rules to ensure DnsClient is mocked out.
-  MockDnsClientRuleList rules;
-  SetMockDnsRules(std::move(rules));
-
-  URLRequestContext context;
-  auto resolve_context =
-      std::make_unique<ResolveContext>(&context, false /* enable_caching */);
-  auto resolver = std::make_unique<ContextHostResolver>(
-      manager_.get(), std::move(resolve_context));
-
-  std::unique_ptr<HostResolver::ProbeRequest> request =
-      resolver->CreateDohProbeRequest();
-
-  request->Start();
-  ASSERT_TRUE(dns_client_->factory()->doh_probes_running());
-
-  resolver->OnShutdown();
-
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-}
-
 TEST_F(ContextHostResolverTest, OnShutdown_CompletedRequests) {
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
@@ -561,11 +516,11 @@
   TestCompletionCallback callback2;
   int rv2 = request2->Start(callback2.callback());
 
-  EXPECT_THAT(callback1.GetResult(rv1), test::IsError(ERR_NAME_NOT_RESOLVED));
+  EXPECT_THAT(callback1.GetResult(rv1), test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_THAT(request1->GetResolveErrorInfo().error,
               test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_FALSE(request1->GetAddressResults());
-  EXPECT_THAT(callback2.GetResult(rv2), test::IsError(ERR_NAME_NOT_RESOLVED));
+  EXPECT_THAT(callback2.GetResult(rv2), test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_THAT(request2->GetResolveErrorInfo().error,
               test::IsError(ERR_CONTEXT_SHUT_DOWN));
   EXPECT_FALSE(request2->GetAddressResults());
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 76320ce..70bc778 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -902,16 +902,17 @@
 // servers to determine availability.
 //
 // Expected to be contained in request classes owned externally to HostResolver,
-// so no assumptions are made regarding cancellation compared to the DnsSession.
-// Instead, uses WeakPtrs to gracefully clean itself up and stop probing after
-// session destruction.
+// so no assumptions are made regarding cancellation compared to the DnsSession
+// or ResolveContext. Instead, uses WeakPtrs to gracefully clean itself up and
+// stop probing after session or context destruction.
 class DnsOverHttpsProbeRunner : public DnsProbeRunner {
  public:
   DnsOverHttpsProbeRunner(base::WeakPtr<DnsSession> session,
-                          ResolveContext* context)
-      : session_(std::move(session)), context_(context->AsSafeRef()) {
+                          base::WeakPtr<ResolveContext> context)
+      : session_(session), context_(context) {
     DCHECK(session_);
     DCHECK(!session_->config().dns_over_https_servers.empty());
+    DCHECK(context_);
 
     DNSDomainFromDot(kDoHProbeHostname, &formatted_probe_hostname_);
 
@@ -925,6 +926,7 @@
 
   void Start(bool network_change) override {
     DCHECK(session_);
+    DCHECK(context_);
 
     // Start probe sequences for any servers where it is not currently running.
     for (size_t i = 0; i < session_->config().dns_over_https_servers.size();
@@ -963,8 +965,9 @@
                      base::WeakPtr<ProbeStats> probe_stats,
                      bool network_change,
                      base::TimeTicks sequence_start_time) {
-    // If the DnsSession has been destroyed, no reason to continue probing.
-    if (!session_) {
+    // If the DnsSession or ResolveContext has been destroyed, no reason to
+    // continue probing.
+    if (!session_ || !context_) {
       probe_stats_list_.clear();
       return;
     }
@@ -1018,7 +1021,7 @@
                      base::TimeTicks query_start_time,
                      int rv) {
     bool success = false;
-    if (rv == OK && probe_stats && session_) {
+    if (rv == OK && probe_stats && session_ && context_) {
       // Check that the response parses properly before considering it a
       // success.
       DCHECK_LT(attempt_number, probe_stats->probe_attempts.size());
@@ -1060,7 +1063,7 @@
   }
 
   base::WeakPtr<DnsSession> session_;
-  base::SafeRef<ResolveContext> context_;
+  base::WeakPtr<ResolveContext> context_;
   std::string formatted_probe_hostname_;
 
   // List of ProbeStats, one for each DoH server, indexed by the DoH server
@@ -1708,8 +1711,8 @@
 
   std::unique_ptr<DnsProbeRunner> CreateDohProbeRunner(
       ResolveContext* resolve_context) override {
-    return std::make_unique<DnsOverHttpsProbeRunner>(session_->GetWeakPtr(),
-                                                     resolve_context);
+    return std::make_unique<DnsOverHttpsProbeRunner>(
+        session_->GetWeakPtr(), resolve_context->GetWeakPtr());
   }
 
   void AddEDNSOption(const OptRecordRdata::Opt& opt) override {
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 3ad2aac..2a94c6ce 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -3583,23 +3583,23 @@
   EXPECT_EQ(runner2->GetDelayUntilNextProbeForTest(0), base::TimeDelta());
 }
 
-TEST_F(DnsTransactionTestWithMockTime, CancelDohProbe) {
-  ConfigureDohServers(true /* use_post */, 1 /* num_doh_servers */,
-                      false /* make_available */);
-  AddQueryAndErrorResponse(0 /* id */, kT4HostName, kT4Qtype,
+TEST_F(DnsTransactionTestWithMockTime, CancelDohProbeOnDestruction) {
+  ConfigureDohServers(/*use_post=*/true, /*num_doh_servers=*/1,
+                      /*make_available=*/false);
+  AddQueryAndErrorResponse(/*id=*/0, kT4HostName, kT4Qtype,
                            ERR_CONNECTION_REFUSED, SYNCHRONOUS,
-                           Transport::HTTPS, nullptr /* opt_rdata */,
+                           Transport::HTTPS, /*opt_rdata=*/nullptr,
                            DnsQuery::PaddingStrategy::BLOCK_LENGTH_128,
-                           false /* enqueue_transaction_id */);
-  AddQueryAndErrorResponse(0 /* id */, kT4HostName, kT4Qtype,
+                           /*enqueue_transaction_id=*/false);
+  AddQueryAndErrorResponse(/*id=*/0, kT4HostName, kT4Qtype,
                            ERR_CONNECTION_REFUSED, SYNCHRONOUS,
-                           Transport::HTTPS, nullptr /* opt_rdata */,
+                           Transport::HTTPS, /*opt_rdata=*/nullptr,
                            DnsQuery::PaddingStrategy::BLOCK_LENGTH_128,
-                           false /* enqueue_transaction_id */);
+                           /* enqueue_transaction_id=*/false);
 
   std::unique_ptr<DnsProbeRunner> runner =
       transaction_factory_->CreateDohProbeRunner(resolve_context_.get());
-  runner->Start(false /* network_change */);
+  runner->Start(/*network_change=*/false);
 
   // The first probe happens without any delay.
   RunUntilIdle();
@@ -3624,6 +3624,46 @@
   EXPECT_FALSE(doh_itr->AttemptAvailable());
 }
 
+TEST_F(DnsTransactionTestWithMockTime, CancelDohProbeOnContextDestruction) {
+  ConfigureDohServers(/*use_post=*/true, /*num_doh_servers=*/1,
+                      /*make_available=*/false);
+  AddQueryAndErrorResponse(/*id=*/0, kT4HostName, kT4Qtype,
+                           ERR_CONNECTION_REFUSED, SYNCHRONOUS,
+                           Transport::HTTPS, /*opt_rdata=*/nullptr,
+                           DnsQuery::PaddingStrategy::BLOCK_LENGTH_128,
+                           /*enqueue_transaction_id=*/false);
+  AddQueryAndErrorResponse(/*id=*/0, kT4HostName, kT4Qtype,
+                           ERR_CONNECTION_REFUSED, SYNCHRONOUS,
+                           Transport::HTTPS, /*opt_rdata=*/nullptr,
+                           DnsQuery::PaddingStrategy::BLOCK_LENGTH_128,
+                           /* enqueue_transaction_id=*/false);
+
+  std::unique_ptr<DnsProbeRunner> runner =
+      transaction_factory_->CreateDohProbeRunner(resolve_context_.get());
+  runner->Start(/*network_change=*/false);
+
+  // The first probe happens without any delay.
+  RunUntilIdle();
+  std::unique_ptr<DnsServerIterator> doh_itr = resolve_context_->GetDohIterator(
+      session_->config(), SecureDnsMode::kAutomatic, session_.get());
+
+  EXPECT_FALSE(doh_itr->AttemptAvailable());
+
+  // Expect the server to still be unavailable after the second probe.
+  FastForwardBy(runner->GetDelayUntilNextProbeForTest(0));
+
+  EXPECT_FALSE(doh_itr->AttemptAvailable());
+
+  base::TimeDelta next_delay = runner->GetDelayUntilNextProbeForTest(0);
+  resolve_context_.reset();
+
+  // The probe detects that the context no longer exists and stops running.
+  FastForwardBy(next_delay);
+
+  // There are no more probes to run.
+  EXPECT_EQ(base::TimeDelta(), runner->GetDelayUntilNextProbeForTest(0));
+}
+
 TEST_F(DnsTransactionTestWithMockTime, CancelOneOfMultipleProbeRunners) {
   ConfigureDohServers(true /* use_post */, 1 /* num_doh_servers */,
                       false /* make_available */);
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 3d79980..2930f88 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/check_op.h"
 #include "base/compiler_specific.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_set.h"
@@ -27,6 +28,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/safe_ref.h"
+#include "base/memory/weak_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
@@ -54,12 +56,15 @@
 #include "build/build_config.h"
 #include "net/base/address_family.h"
 #include "net/base/address_list.h"
+#include "net/base/completion_once_callback.h"
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_interfaces.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/prioritized_dispatcher.h"
 #include "net/base/request_priority.h"
 #include "net/base/trace_constants.h"
 #include "net/base/url_util.h"
@@ -637,14 +642,14 @@
 // cancellation is initiated by the Job (OnJobCancelled) vs by the end user
 // (~RequestImpl).
 class HostResolverManager::RequestImpl
-    : public CancellableResolveHostRequest,
+    : public HostResolver::ResolveHostRequest,
       public base::LinkNode<HostResolverManager::RequestImpl> {
  public:
   RequestImpl(NetLogWithSource source_net_log,
               absl::variant<url::SchemeHostPort, HostPortPair> request_host,
               NetworkIsolationKey network_isolation_key,
               absl::optional<ResolveHostParameters> optional_parameters,
-              ResolveContext* resolve_context,
+              base::WeakPtr<ResolveContext> resolve_context,
               HostCache* host_cache,
               base::WeakPtr<HostResolverManager> resolver,
               const base::TickClock* tick_clock)
@@ -657,25 +662,20 @@
                 : NetworkIsolationKey()),
         parameters_(optional_parameters ? std::move(optional_parameters).value()
                                         : ResolveHostParameters()),
-        resolve_context_(resolve_context->AsSafeRef()),
+        resolve_context_(std::move(resolve_context)),
         host_cache_(host_cache),
         host_resolver_flags_(
             HostResolver::ParametersToHostResolverFlags(parameters_)),
         priority_(parameters_.initial_priority),
         job_(nullptr),
-        resolver_(resolver),
+        resolver_(std::move(resolver)),
         complete_(false),
         tick_clock_(tick_clock) {}
 
   RequestImpl(const RequestImpl&) = delete;
   RequestImpl& operator=(const RequestImpl&) = delete;
 
-  ~RequestImpl() override {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    Cancel();
-  }
-
-  void Cancel() override;
+  ~RequestImpl() override;
 
   int Start(CompletionOnceCallback callback) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -687,6 +687,13 @@
     // Parent HostResolver must still be alive to call Start().
     DCHECK(resolver_);
 
+    if (!resolve_context_) {
+      complete_ = true;
+      resolver_ = nullptr;
+      set_error_info(ERR_CONTEXT_SHUT_DOWN, false);
+      return ERR_NAME_NOT_RESOLVED;
+    }
+
     LogStartRequest();
     int rv = resolver_->Resolve(this);
     DCHECK(!complete_);
@@ -843,7 +850,7 @@
 
   const ResolveHostParameters& parameters() const { return parameters_; }
 
-  ResolveContext* resolve_context() const { return &*resolve_context_; }
+  ResolveContext* resolve_context() const { return resolve_context_.get(); }
 
   HostCache* host_cache() const { return host_cache_; }
 
@@ -918,7 +925,7 @@
   const absl::variant<url::SchemeHostPort, HostPortPair> request_host_;
   const NetworkIsolationKey network_isolation_key_;
   ResolveHostParameters parameters_;
-  base::SafeRef<ResolveContext> resolve_context_;
+  base::WeakPtr<ResolveContext> resolve_context_;
   const raw_ptr<HostCache> host_cache_;
   const HostResolverFlags host_resolver_flags_;
 
@@ -944,32 +951,30 @@
 };
 
 class HostResolverManager::ProbeRequestImpl
-    : public CancellableProbeRequest,
+    : public HostResolver::ProbeRequest,
       public ResolveContext::DohStatusObserver {
  public:
-  ProbeRequestImpl(ResolveContext* context,
+  ProbeRequestImpl(base::WeakPtr<ResolveContext> context,
                    base::WeakPtr<HostResolverManager> resolver)
-      : context_(context->AsSafeRef()), resolver_(resolver) {}
+      : context_(std::move(context)), resolver_(std::move(resolver)) {}
 
   ProbeRequestImpl(const ProbeRequestImpl&) = delete;
   ProbeRequestImpl& operator=(const ProbeRequestImpl&) = delete;
 
-  ~ProbeRequestImpl() override { Cancel(); }
-
-  void Cancel() override {
-    runner_.reset();
-
-    if (context_.has_value())
-      context_.value()->UnregisterDohStatusObserver(this);
-    context_.reset();
+  ~ProbeRequestImpl() override {
+    // Ensure that observers are deregistered to avoid wasting memory.
+    if (context_)
+      context_->UnregisterDohStatusObserver(this);
   }
 
   int Start() override {
     DCHECK(resolver_);
-    DCHECK(context_.has_value());
     DCHECK(!runner_);
 
-    context_.value()->RegisterDohStatusObserver(this);
+    if (!context_)
+      return ERR_CONTEXT_SHUT_DOWN;
+
+    context_->RegisterDohStatusObserver(this);
 
     StartRunner(false /* network_change */);
     return ERR_IO_PENDING;
@@ -992,8 +997,11 @@
     DCHECK(resolver_);
     DCHECK(!resolver_->invalidation_in_progress_);
 
+    if (!context_)
+      return;  // Reachable if the context ends before a posted task runs.
+
     if (!runner_)
-      runner_ = resolver_->CreateDohProbeRunner(&*context_.value());
+      runner_ = resolver_->CreateDohProbeRunner(context_.get());
     if (runner_)
       runner_->Start(network_change);
   }
@@ -1005,9 +1013,7 @@
     weak_ptr_factory_.InvalidateWeakPtrs();
   }
 
-  // nullopt after cancellation, but otherwise expected to contain a still-valid
-  // ResolveContext reference.
-  absl::optional<base::SafeRef<ResolveContext>> context_;
+  base::WeakPtr<ResolveContext> context_;
 
   std::unique_ptr<DnsProbeRunner> runner_;
   base::WeakPtr<HostResolverManager> resolver_;
@@ -1999,10 +2005,9 @@
         proc_task_runner_(std::move(proc_task_runner)),
         had_non_speculative_request_(false),
         num_occupied_job_slots_(0),
-        dispatcher_(nullptr),
+        dispatched_(false),
         dns_task_error_(OK),
         tick_clock_(tick_clock),
-        start_time_(base::TimeTicks()),
         net_log_(
             NetLogWithSource::Make(source_net_log.net_log(),
                                    NetLogSourceType::HOST_RESOLVER_IMPL_JOB)) {
@@ -2014,16 +2019,16 @@
   }
 
   ~Job() override {
-    if (is_running()) {
-      // |resolver_| was destroyed with this Job still in flight.
-      // Clean-up, record in the log, but don't run any callbacks.
-      proc_task_ = nullptr;
-      // Clean up now for nice NetLog.
-      KillDnsTask();
+    bool was_queued = is_queued();
+    bool was_running = is_running();
+    // Clean up now for nice NetLog.
+    Finish();
+    if (was_running) {
+      // This Job was destroyed while still in flight.
       net_log_.EndEventWithNetErrorCode(
           NetLogEventType::HOST_RESOLVER_MANAGER_JOB, ERR_ABORTED);
-    } else if (is_queued()) {
-      // |resolver_| was destroyed without running this Job.
+    } else if (was_queued) {
+      // Job was cancelled before it could run.
       // TODO(szym): is there any benefit in having this distinction?
       net_log_.AddEvent(NetLogEventType::CANCELLED);
       net_log_.EndEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB);
@@ -2043,11 +2048,11 @@
   void Schedule(bool at_head) {
     DCHECK(!is_queued());
     PrioritizedDispatcher::Handle handle;
-    DCHECK(dispatcher_);
+    DCHECK(dispatched_);
     if (!at_head) {
-      handle = dispatcher_->Add(this, priority());
+      handle = resolver_->dispatcher_->Add(this, priority());
     } else {
-      handle = dispatcher_->AddAtHead(this, priority());
+      handle = resolver_->dispatcher_->AddAtHead(this, priority());
     }
     // The dispatcher could have started |this| in the above call to Add, which
     // could have called Schedule again. In that case |handle| will be null,
@@ -2245,17 +2250,18 @@
     TaskType next_task = tasks_.front();
 
     // Schedule insecure DnsTasks and ProcTasks with the dispatcher.
-    if (!dispatcher_ &&
+    if (!dispatched_ &&
         (next_task == TaskType::DNS || next_task == TaskType::PROC ||
          next_task == TaskType::MDNS)) {
-      dispatcher_ = resolver_->dispatcher_.get();
+      dispatched_ = true;
       job_running_ = false;
       Schedule(false);
       DCHECK(is_running() || is_queued());
 
       // Check for queue overflow.
-      if (dispatcher_->num_queued_jobs() > resolver_->max_queued_jobs_) {
-        Job* evicted = static_cast<Job*>(dispatcher_->EvictOldestLowest());
+      PrioritizedDispatcher& dispatcher = *resolver_->dispatcher_;
+      if (dispatcher.num_queued_jobs() > resolver_->max_queued_jobs_) {
+        Job* evicted = static_cast<Job*>(dispatcher.EvictOldestLowest());
         DCHECK(evicted);
         evicted->OnEvicted();
       }
@@ -2310,9 +2316,35 @@
     return dict;
   }
 
+  void Finish() {
+    if (is_running()) {
+      // Clean up but don't run any callbacks.
+      proc_task_ = nullptr;
+      KillDnsTask();
+      mdns_task_ = nullptr;
+      job_running_ = false;
+
+      if (dispatched_) {
+        // Job should only ever occupy one slot after any tasks that may have
+        // required additional slots, e.g. DnsTask, have been killed, and
+        // additional slots are expected to be vacated as part of killing the
+        // task.
+        DCHECK_EQ(1, num_occupied_job_slots_);
+        if (resolver_)
+          resolver_->dispatcher_->OnJobFinished();
+        num_occupied_job_slots_ = 0;
+      }
+    } else if (is_queued()) {
+      DCHECK(dispatched_);
+      if (resolver_)
+        resolver_->dispatcher_->Cancel(handle_);
+      handle_.Reset();
+    }
+  }
+
   void KillDnsTask() {
     if (dns_task_) {
-      if (dispatcher_) {
+      if (dispatched_) {
         while (num_occupied_job_slots_ > 1 || is_queued()) {
           ReduceByOneJobSlot();
         }
@@ -2327,12 +2359,14 @@
   // so signals it is complete.
   void ReduceByOneJobSlot() {
     DCHECK_GE(num_occupied_job_slots_, 1);
-    DCHECK(dispatcher_);
+    DCHECK(dispatched_);
     if (is_queued()) {
-      dispatcher_->Cancel(handle_);
+      if (resolver_)
+        resolver_->dispatcher_->Cancel(handle_);
       handle_.Reset();
     } else if (num_occupied_job_slots_ > 1) {
-      dispatcher_->OnJobFinished();
+      if (resolver_)
+        resolver_->dispatcher_->OnJobFinished();
       --num_occupied_job_slots_;
     } else {
       NOTREACHED();
@@ -2340,8 +2374,8 @@
   }
 
   void UpdatePriority() {
-    if (is_queued() && dispatcher_)
-      handle_ = dispatcher_->ChangePriority(handle_, priority());
+    if (is_queued())
+      handle_ = resolver_->dispatcher_->ChangePriority(handle_, priority());
   }
 
   // PrioritizedDispatcher::Job:
@@ -2351,10 +2385,9 @@
 
     if (num_occupied_job_slots_ >= 2) {
       if (!dns_task_) {
-        dispatcher_->OnJobFinished();
+        resolver_->dispatcher_->OnJobFinished();
         return;
       }
-      DCHECK(dns_task_);
       StartNextDnsTransaction();
       if (dns_task_->needs_another_transaction()) {
         Schedule(true);
@@ -2373,7 +2406,7 @@
   // ThreadPool threads low, we will need to use an "inner"
   // PrioritizedDispatcher with tighter limits.
   void StartProcTask() {
-    DCHECK(dispatcher_);
+    DCHECK(dispatched_);
     DCHECK_EQ(1, num_occupied_job_slots_);
     DCHECK(IsAddressType(key_.query_type));
 
@@ -2440,8 +2473,8 @@
   }
 
   void StartDnsTask(bool secure) {
-    DCHECK_EQ(secure, !dispatcher_);
-    DCHECK_EQ(dispatcher_ ? 1 : 0, num_occupied_job_slots_);
+    DCHECK_EQ(secure, !dispatched_);
+    DCHECK_EQ(dispatched_ ? 1 : 0, num_occupied_job_slots_);
     DCHECK(!resolver_->HaveTestProcOverride());
     // Need to create the task even if we're going to post a failure instead of
     // running it, as a "started" job needs a task to be properly cleaned up.
@@ -2461,9 +2494,9 @@
   }
 
   void StartNextDnsTransaction() {
-    DCHECK_EQ(dns_task_->secure(), !dispatcher_);
-    DCHECK(!dispatcher_ || num_occupied_job_slots_ >= 1);
     DCHECK(dns_task_);
+    DCHECK_EQ(dns_task_->secure(), !dispatched_);
+    DCHECK(!dispatched_ || num_occupied_job_slots_ >= 1);
     DCHECK(dns_task_->needs_another_transaction());
     dns_task_->StartNextTransaction();
   }
@@ -2553,7 +2586,7 @@
     DCHECK_LE(2, dns_task_->num_needed_transactions());
     DCHECK_EQ(dns_task_->needs_another_transaction(), is_queued());
 
-    if (dispatcher_) {
+    if (dispatched_) {
       // We already have a job slot at the dispatcher, so if the next
       // transaction hasn't started, reuse it now instead of waiting in the
       // queue for another slot.
@@ -2564,7 +2597,7 @@
       } else {
         dns_task_->StartNextTransaction();
         if (!dns_task_->needs_another_transaction() && is_queued()) {
-          dispatcher_->Cancel(handle_);
+          resolver_->dispatcher_->Cancel(handle_);
           handle_.Reset();
         }
       }
@@ -2713,26 +2746,7 @@
     if (self_iterator_)
       self_deleter = resolver_->RemoveJob(self_iterator_.value());
 
-    if (is_running()) {
-      proc_task_ = nullptr;
-      KillDnsTask();
-      mdns_task_ = nullptr;
-      job_running_ = false;
-
-      if (dispatcher_) {
-        // Job should only ever occupy one slot after any tasks that may have
-        // required additional slots, e.g. DnsTask, have been killed, and
-        // additional slots are expected to be vacated as part of killing the
-        // task.
-        DCHECK_EQ(1, num_occupied_job_slots_);
-        // Signal dispatcher that a slot has opened.
-        dispatcher_->OnJobFinished();
-      }
-    } else if (is_queued()) {
-      DCHECK(dispatcher_);
-      dispatcher_->Cancel(handle_);
-      handle_.Reset();
-    }
+    Finish();
 
     if (num_active_requests() == 0) {
       net_log_.AddEvent(NetLogEventType::CANCELLED);
@@ -2847,9 +2861,8 @@
   // the job is not registered with any dispatcher.
   int num_occupied_job_slots_;
 
-  // The dispatcher with which this Job is currently registered. Is nullptr if
-  // not registered with any dispatcher.
-  raw_ptr<PrioritizedDispatcher> dispatcher_;
+  // True once this Job has been sent to `resolver_->dispatcher_`.
+  bool dispatched_;
 
   // Result of DnsTask.
   int dns_task_error_;
@@ -2953,7 +2966,7 @@
     system_dns_config_notifier_->RemoveObserver(this);
 }
 
-std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
+std::unique_ptr<HostResolver::ResolveHostRequest>
 HostResolverManager::CreateRequest(
     absl::variant<url::SchemeHostPort, HostPortPair> host,
     NetworkIsolationKey network_isolation_key,
@@ -2970,15 +2983,15 @@
 
   return std::make_unique<RequestImpl>(
       std::move(net_log), std::move(host), std::move(network_isolation_key),
-      std::move(optional_parameters), resolve_context, host_cache,
+      std::move(optional_parameters), resolve_context->GetWeakPtr(), host_cache,
       weak_ptr_factory_.GetWeakPtr(), tick_clock_);
 }
 
-std::unique_ptr<HostResolverManager::CancellableProbeRequest>
+std::unique_ptr<HostResolver::ProbeRequest>
 HostResolverManager::CreateDohProbeRequest(ResolveContext* context) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  return std::make_unique<ProbeRequestImpl>(context,
+  return std::make_unique<ProbeRequestImpl>(context->GetWeakPtr(),
                                             weak_ptr_factory_.GetWeakPtr());
 }
 
@@ -3080,6 +3093,9 @@
 void HostResolverManager::DeregisterResolveContext(
     const ResolveContext* context) {
   registered_contexts_.RemoveObserver(context);
+
+  // Destroy Jobs when their context is closed.
+  RemoveAllJobs(context);
 }
 
 void HostResolverManager::SetTickClockForTesting(
@@ -3764,6 +3780,17 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void HostResolverManager::RemoveAllJobs(const ResolveContext* context) {
+  for (auto it = jobs_.begin(); it != jobs_.end();) {
+    const JobKey& key = it->first;
+    if (&*key.resolve_context == context) {
+      RemoveJob(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
 void HostResolverManager::AbortAllJobs(bool in_progress_only) {
   // In Abort, a Request callback could spawn new Jobs with matching keys, so
   // first collect and remove all running jobs from |jobs_|.
@@ -3968,6 +3995,8 @@
 
 std::unique_ptr<DnsProbeRunner> HostResolverManager::CreateDohProbeRunner(
     ResolveContext* resolve_context) {
+  DCHECK(resolve_context);
+  DCHECK(registered_contexts_.HasObserver(resolve_context));
   if (!dns_client_->CanUseSecureDnsTransactions())
     return nullptr;
 
@@ -3975,15 +4004,12 @@
       resolve_context);
 }
 
-void HostResolverManager::RequestImpl::Cancel() {
+HostResolverManager::RequestImpl::~RequestImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!job_)
     return;
 
   job_->CancelRequest(this);
-  job_ = nullptr;
-  callback_.Reset();
-
   LogCancelRequest();
 }
 
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 254f809..6af3325 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -101,29 +101,6 @@
   using MdnsListener = HostResolver::MdnsListener;
   using ResolveHostParameters = HostResolver::ResolveHostParameters;
 
-  // A request that allows explicit cancellation before destruction. Enables
-  // callers (e.g. ContextHostResolver) to implement cancellation of requests on
-  // the callers' destruction.
-  class CancellableRequest {
-   public:
-    CancellableRequest() = default;
-    CancellableRequest(const CancellableRequest&) = delete;
-    CancellableRequest& operator=(const CancellableRequest&) = delete;
-    virtual ~CancellableRequest() = default;
-
-    // If running asynchronously, silently cancels the request as if destroyed.
-    // Callbacks will never be invoked. Noop if request is already complete or
-    // never started.
-    virtual void Cancel() = 0;
-  };
-
-  // CancellableRequest versions of different request types.
-  class CancellableResolveHostRequest
-      : public CancellableRequest,
-        public HostResolver::ResolveHostRequest {};
-  class CancellableProbeRequest : public CancellableRequest,
-                                  public HostResolver::ProbeRequest {};
-
   // Creates a HostResolver as specified by |options|. Blocking tasks are run in
   // ThreadPool.
   //
@@ -156,7 +133,7 @@
   //
   // TODO(crbug.com/1022059): Use the HostCache out of the ResolveContext
   // instead of passing it separately.
-  std::unique_ptr<CancellableResolveHostRequest> CreateRequest(
+  std::unique_ptr<HostResolver::ResolveHostRequest> CreateRequest(
       absl::variant<url::SchemeHostPort, HostPortPair> host,
       NetworkIsolationKey network_isolation_key,
       NetLogWithSource net_log,
@@ -165,8 +142,8 @@
       HostCache* host_cache);
   // |resolve_context| is the context to use for the probes, and it is expected
   // to be the context of the calling ContextHostResolver.
-  std::unique_ptr<CancellableProbeRequest> CreateDohProbeRequest(
-      ResolveContext* resolvet_context);
+  std::unique_ptr<HostResolver::ProbeRequest> CreateDohProbeRequest(
+      ResolveContext* resolve_context);
   std::unique_ptr<MdnsListener> CreateMdnsListener(const HostPortPair& host,
                                                    DnsQueryType query_type);
 
@@ -401,6 +378,9 @@
   // Removes |job_it| from |jobs_| and return.
   std::unique_ptr<Job> RemoveJob(JobMap::iterator job_it);
 
+  // Removes Jobs for this context.
+  void RemoveAllJobs(const ResolveContext* context);
+
   // Aborts both scheduled and running jobs with ERR_NETWORK_CHANGED and
   // notifies their requests. Aborts only running jobs if |in_progress_only| is
   // true. Might start new jobs.
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 42630ff..fcf5c1f 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/dns/host_resolver_manager.h"
 
 #include <algorithm>
+#include <iterator>
 #include <limits>
 #include <string>
 #include <tuple>
@@ -283,15 +284,13 @@
 
   ResolveHostResponseHelper() = default;
   explicit ResolveHostResponseHelper(
-      std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-          request)
+      std::unique_ptr<HostResolver::ResolveHostRequest> request)
       : request_(std::move(request)) {
     top_level_result_error_ = request_->Start(base::BindOnce(
         &ResolveHostResponseHelper::OnComplete, base::Unretained(this)));
   }
   ResolveHostResponseHelper(
-      std::unique_ptr<HostResolverManager::CancellableResolveHostRequest>
-          request,
+      std::unique_ptr<HostResolver::ResolveHostRequest> request,
       Callback custom_callback)
       : request_(std::move(request)) {
     top_level_result_error_ = request_->Start(
@@ -316,9 +315,7 @@
     return request_->GetResolveErrorInfo().error;
   }
 
-  HostResolverManager::CancellableResolveHostRequest* request() {
-    return request_.get();
-  }
+  HostResolver::ResolveHostRequest* request() { return request_.get(); }
 
   void CancelRequest() {
     DCHECK(request_);
@@ -344,7 +341,7 @@
     DCHECK(complete());
   }
 
-  std::unique_ptr<HostResolverManager::CancellableResolveHostRequest> request_;
+  std::unique_ptr<HostResolver::ResolveHostRequest> request_;
   int top_level_result_error_ = ERR_IO_PENDING;
   base::RunLoop run_loop_;
 };
@@ -5657,61 +5654,6 @@
               CreateExpected("::1", 80), CreateExpected("127.0.0.1", 80))))));
 }
 
-TEST_F(HostResolverManagerDnsTest, ExplicitCancel) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  ResolveHostResponseHelper response(resolver_->CreateRequest(
-      HostPortPair("4slow_4ok", 80), NetworkIsolationKey(), NetLogWithSource(),
-      absl::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
-
-  response.request()->Cancel();
-  dns_client_->CompleteDelayedTransactions();
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(response.complete());
-}
-
-TEST_F(HostResolverManagerDnsTest, ExplicitCancel_AfterManagerDestruction) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  ResolveHostResponseHelper response(resolver_->CreateRequest(
-      HostPortPair("4slow_4ok", 80), NetworkIsolationKey(), NetLogWithSource(),
-      absl::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
-
-  DestroyResolver();
-  response.request()->Cancel();
-}
-
-TEST_F(HostResolverManagerDnsTest, ExplicitCancel_Completed) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  ResolveHostResponseHelper response(resolver_->CreateRequest(
-      HostPortPair("ok", 80), NetworkIsolationKey(), NetLogWithSource(),
-      absl::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
-
-  EXPECT_THAT(response.result_error(), IsOk());
-  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
-              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
-                                            CreateExpected("::1", 80)));
-  EXPECT_THAT(
-      response.request()->GetEndpointResults(),
-      testing::Optional(testing::ElementsAre(
-          ExpectEndpointResult(testing::UnorderedElementsAre(
-              CreateExpected("::1", 80), CreateExpected("127.0.0.1", 80))))));
-
-  response.request()->Cancel();
-
-  // Completed requests should be unaffected by cancellation.
-  EXPECT_THAT(response.request()->GetAddressResults().value().endpoints(),
-              testing::UnorderedElementsAre(CreateExpected("127.0.0.1", 80),
-                                            CreateExpected("::1", 80)));
-  EXPECT_THAT(
-      response.request()->GetEndpointResults(),
-      testing::Optional(testing::ElementsAre(
-          ExpectEndpointResult(testing::UnorderedElementsAre(
-              CreateExpected("::1", 80), CreateExpected("127.0.0.1", 80))))));
-}
-
 // Cancel a request with only the IPv6 transaction active.
 TEST_F(HostResolverManagerDnsTest, CancelWithIPv6TransactionActive) {
   ChangeDnsConfig(CreateValidDnsConfig());
@@ -13757,7 +13699,7 @@
 
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
+  std::unique_ptr<HostResolver::ProbeRequest> request =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
 
@@ -13768,47 +13710,10 @@
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
 }
 
-TEST_F(HostResolverManagerDnsTest, DohProbeRequest_ExplicitCancel) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
-      resolver_->CreateDohProbeRequest(resolve_context_.get());
-  EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
-  ASSERT_TRUE(dns_client_->factory()->doh_probes_running());
-
-  request->Cancel();
-
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-}
-
-TEST_F(HostResolverManagerDnsTest, DohProbeRequest_ExplicitCancel_NotStarted) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
-      resolver_->CreateDohProbeRequest(resolve_context_.get());
-
-  request->Cancel();
-
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-}
-
-TEST_F(HostResolverManagerDnsTest,
-       DohProbeRequest_ExplicitCancel_AfterManagerDestruction) {
-  ChangeDnsConfig(CreateValidDnsConfig());
-
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
-      resolver_->CreateDohProbeRequest(resolve_context_.get());
-  EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
-  ASSERT_TRUE(dns_client_->factory()->doh_probes_running());
-
-  DestroyResolver();
-  request->Cancel();
-}
-
 TEST_F(HostResolverManagerDnsTest, DohProbeRequest_BeforeConfig) {
   InvalidateDnsConfig();
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
+  std::unique_ptr<HostResolver::ProbeRequest> request =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
@@ -13820,7 +13725,7 @@
 TEST_F(HostResolverManagerDnsTest, DohProbeRequest_InvalidateConfig) {
   ChangeDnsConfig(CreateValidDnsConfig());
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
+  std::unique_ptr<HostResolver::ProbeRequest> request =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
   ASSERT_TRUE(dns_client_->factory()->doh_probes_running());
@@ -13830,20 +13735,6 @@
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
 }
 
-TEST_F(HostResolverManagerDnsTest, DohProbeRequest_CancelBeforeConfig) {
-  InvalidateDnsConfig();
-
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
-      resolver_->CreateDohProbeRequest(resolve_context_.get());
-  EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-
-  request->Cancel();
-
-  ChangeDnsConfig(CreateValidDnsConfig());
-  EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
-}
-
 TEST_F(HostResolverManagerDnsTest, DohProbeRequest_RestartOnConnectionChange) {
   DestroyResolver();
   test::ScopedMockNetworkChangeNotifier notifier;
@@ -13852,7 +13743,7 @@
       NetworkChangeNotifier::CONNECTION_NONE);
   ChangeDnsConfig(CreateValidDnsConfig());
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request =
+  std::unique_ptr<HostResolver::ProbeRequest> request =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request->Start(), IsError(ERR_IO_PENDING));
   EXPECT_TRUE(dns_client_->factory()->doh_probes_running());
@@ -13870,10 +13761,10 @@
 
   EXPECT_FALSE(dns_client_->factory()->doh_probes_running());
 
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request1 =
+  std::unique_ptr<HostResolver::ProbeRequest> request1 =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request1->Start(), IsError(ERR_IO_PENDING));
-  std::unique_ptr<HostResolverManager::CancellableProbeRequest> request2 =
+  std::unique_ptr<HostResolver::ProbeRequest> request2 =
       resolver_->CreateDohProbeRequest(resolve_context_.get());
   EXPECT_THAT(request2->Start(), IsError(ERR_IO_PENDING));
 
diff --git a/net/dns/resolve_context.h b/net/dns/resolve_context.h
index e9c6d94..19b657d 100644
--- a/net/dns/resolve_context.h
+++ b/net/dns/resolve_context.h
@@ -177,10 +177,14 @@
   // (alternative service info if it supports QUIC, for instance).
   const IsolationInfo& isolation_info() const { return isolation_info_; }
 
-  base::SafeRef<ResolveContext> AsSafeRef() const {
+  base::SafeRef<ResolveContext> AsSafeRef() {
     return weak_ptr_factory_.GetSafeRef();
   }
 
+  base::WeakPtr<ResolveContext> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  private:
   friend DohDnsServerIterator;
   friend ClassicDnsServerIterator;
@@ -256,8 +260,10 @@
   // Current maximum server fallback period. Updated on connection change.
   base::TimeDelta max_fallback_period_;
 
+  // All DohStatusObservers only hold a WeakPtr<ResolveContext>, so there's no
+  // need for check_empty to be true.
   base::ObserverList<DohStatusObserver,
-                     true /* check_empty */,
+                     false /* check_empty */,
                      false /* allow_reentrancy */>
       doh_status_observers_;
 
diff --git a/net/nqe/network_qualities_prefs_manager.cc b/net/nqe/network_qualities_prefs_manager.cc
index e8a3533..7f06ffe 100644
--- a/net/nqe/network_qualities_prefs_manager.cc
+++ b/net/nqe/network_qualities_prefs_manager.cc
@@ -99,7 +99,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   LOCAL_HISTOGRAM_COUNTS_100("NQE.PrefsSizeOnClearing", prefs_->DictSize());
-  prefs_->Clear();
+  prefs_->DictClear();
   DCHECK_EQ(0u, prefs_->DictSize());
   pref_delegate_->SetDictionaryValue(*prefs_);
 }
diff --git a/net/nqe/network_qualities_prefs_manager_unittest.cc b/net/nqe/network_qualities_prefs_manager_unittest.cc
index 1828c25..56518661 100644
--- a/net/nqe/network_qualities_prefs_manager_unittest.cc
+++ b/net/nqe/network_qualities_prefs_manager_unittest.cc
@@ -35,7 +35,7 @@
 
   ~TestPrefDelegate() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    value_->Clear();
+    value_->DictClear();
     EXPECT_EQ(0U, value_->DictSize());
   }
 
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
index 877f5aa..54dd798f 100644
--- a/printing/printed_document.cc
+++ b/printing/printed_document.cc
@@ -159,7 +159,7 @@
   return page;
 }
 
-void PrintedDocument::DropPage(const PrintedPage* page) {
+void PrintedDocument::RemovePage(const PrintedPage* page) {
   base::AutoLock lock(lock_);
   PrintedPages::const_iterator it =
       mutable_.pages_.find(page->page_number() - 1);
diff --git a/printing/printed_document.h b/printing/printed_document.h
index de9e6651d..ceecca9 100644
--- a/printing/printed_document.h
+++ b/printing/printed_document.h
@@ -62,9 +62,9 @@
   // Note: locks for a short amount of time.
   scoped_refptr<PrintedPage> GetPage(uint32_t page_number);
 
-  // Drop the specified page's reference for the particular page number.
+  // Removes reference to a particular `page` based on its page number.
   // Note: locks for a short amount of time.
-  void DropPage(const PrintedPage* page);
+  void RemovePage(const PrintedPage* page);
 #endif  // defined(OS_WIN)
 
   // Sets the document data. Note: locks for a short amount of time.
diff --git a/remoting/host/policy_watcher.cc b/remoting/host/policy_watcher.cc
index dbfe11f..588c211 100644
--- a/remoting/host/policy_watcher.cc
+++ b/remoting/host/policy_watcher.cc
@@ -203,8 +203,8 @@
 }
 
 void PolicyWatcher::SignalPolicyError() {
-  effective_policies_->Clear();
-  platform_policies_->Clear();
+  effective_policies_->DictClear();
+  platform_policies_->DictClear();
   policy_error_callback_.Run();
 }
 
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index d9ec13e..cbc6aceb 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -498,7 +498,7 @@
   message.SetString("pin", "1234");
   WriteMessageToInputPipe(message);
 
-  message.Clear();
+  message.DictClear();
   message.SetInteger("id", next_id++);
   message.SetString("type", "generateKeyPair");
   WriteMessageToInputPipe(message);
@@ -527,7 +527,7 @@
   message.SetString("type", "updateDaemonConfig");
   WriteMessageToInputPipe(message);
 
-  config.Clear();
+  config.DictClear();
   config.SetBoolean("start", true);
   message.Set("config", config.CreateDeepCopy());
   message.SetBoolean("consent", true);
diff --git a/remoting/host/webauthn/remote_webauthn_constants.cc b/remoting/host/webauthn/remote_webauthn_constants.cc
index 35eeeea..134c56f 100644
--- a/remoting/host/webauthn/remote_webauthn_constants.cc
+++ b/remoting/host/webauthn/remote_webauthn_constants.cc
@@ -7,7 +7,11 @@
 namespace remoting {
 
 const char kRemoteWebAuthnDataChannelName[] = "remote-webauthn";
+
 const char kIsUvpaaMessageType[] = "isUvpaa";
+const char kGetRemoteStateMessageType[] = "getRemoteState";
+
 const char kIsUvpaaResponseIsAvailableKey[] = "isAvailable";
+const char kGetRemoteStateResponseIsRemotedKey[] = "isRemoted";
 
 }  // namespace remoting
diff --git a/remoting/host/webauthn/remote_webauthn_constants.h b/remoting/host/webauthn/remote_webauthn_constants.h
index a1555ee4..65ac883 100644
--- a/remoting/host/webauthn/remote_webauthn_constants.h
+++ b/remoting/host/webauthn/remote_webauthn_constants.h
@@ -11,9 +11,11 @@
 
 // NMH message types.
 extern const char kIsUvpaaMessageType[];
+extern const char kGetRemoteStateMessageType[];
 
 // NMH message keys.
 extern const char kIsUvpaaResponseIsAvailableKey[];
+extern const char kGetRemoteStateResponseIsRemotedKey[];
 
 }  // namespace remoting
 
diff --git a/remoting/host/webauthn/remote_webauthn_native_messaging_host.cc b/remoting/host/webauthn/remote_webauthn_native_messaging_host.cc
index 07d13be..7faf2eb 100644
--- a/remoting/host/webauthn/remote_webauthn_native_messaging_host.cc
+++ b/remoting/host/webauthn/remote_webauthn_native_messaging_host.cc
@@ -9,12 +9,14 @@
 #include "base/bind.h"
 #include "base/json/json_writer.h"
 #include "base/logging.h"
+#include "base/values.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 #include "remoting/host/native_messaging/native_messaging_constants.h"
 #include "remoting/host/native_messaging/native_messaging_helpers.h"
 #include "remoting/host/webauthn/remote_webauthn_constants.h"
+#include "remoting/host/webauthn/remote_webauthn_message_handler.h"
 
 namespace remoting {
 
@@ -44,6 +46,8 @@
     ProcessHello(std::move(response));
   } else if (type == kIsUvpaaMessageType) {
     ProcessIsUvpaa(request, std::move(response));
+  } else if (type == kGetRemoteStateMessageType) {
+    ProcessGetRemoteState(std::move(response));
   } else {
     LOG(ERROR) << "Unsupported request type: " << type;
   }
@@ -90,19 +94,79 @@
                      base::Unretained(this), std::move(response)));
 }
 
+void RemoteWebAuthnNativeMessagingHost::ProcessGetRemoteState(
+    base::Value response) {
+  // GetRemoteState request: {id: string, type: 'getRemoteState'}
+  // GetRemoteState response: {id: string, type: 'getRemoteStateResponse'}
+
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  // We query and report the remote state one at a time to prevent race
+  // conditions caused by multiple requests coming in while there is already a
+  // pending request (e.g. WebAuthn channel connected and AttachToDesktop on
+  // Windows).
+  get_remote_state_responses_.push(std::move(response));
+  if (get_remote_state_responses_.size() == 1) {
+    QueryNextRemoteState();
+  }
+  // Otherwise it means there is already a pending remote state request.
+}
+
+void RemoteWebAuthnNativeMessagingHost::OnQueryVersionResult(uint32_t version) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  SendNextRemoteState(true);
+}
+
 void RemoteWebAuthnNativeMessagingHost::OnIpcDisconnected() {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
   // TODO(yuweih): Feed pending callbacks with error responses.
   remote_.reset();
+  if (!get_remote_state_responses_.empty()) {
+    SendNextRemoteState(false);
+  }
 }
 
 void RemoteWebAuthnNativeMessagingHost::OnIsUvpaaResponse(base::Value response,
                                                           bool is_available) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
   response.SetBoolKey(kIsUvpaaResponseIsAvailableKey, is_available);
   SendMessageToClient(std::move(response));
 }
 
+void RemoteWebAuthnNativeMessagingHost::QueryNextRemoteState() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  if (!EnsureIpcConnection()) {
+    SendNextRemoteState(false);
+    return;
+  }
+
+  // QueryVersion() is simply used to determine if the receiving end actually
+  // accepts the connection. If it doesn't, then the callback will be silently
+  // dropped, and OnIpcDisconnected() will be called instead.
+  remote_.QueryVersion(
+      base::BindOnce(&RemoteWebAuthnNativeMessagingHost::OnQueryVersionResult,
+                     base::Unretained(this)));
+}
+
+void RemoteWebAuthnNativeMessagingHost::SendNextRemoteState(bool is_remoted) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!get_remote_state_responses_.empty());
+
+  auto response = std::move(get_remote_state_responses_.front());
+  get_remote_state_responses_.pop();
+  DCHECK(response.is_dict());
+
+  response.SetBoolKey(kGetRemoteStateResponseIsRemotedKey, is_remoted);
+  SendMessageToClient(std::move(response));
+  if (!get_remote_state_responses_.empty()) {
+    QueryNextRemoteState();
+  }
+}
+
 bool RemoteWebAuthnNativeMessagingHost::EnsureIpcConnection() {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
diff --git a/remoting/host/webauthn/remote_webauthn_native_messaging_host.h b/remoting/host/webauthn/remote_webauthn_native_messaging_host.h
index 73af648..97daf70 100644
--- a/remoting/host/webauthn/remote_webauthn_native_messaging_host.h
+++ b/remoting/host/webauthn/remote_webauthn_native_messaging_host.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/containers/queue.h"
 #include "base/memory/raw_ptr.h"
 #include "base/values.h"
 #include "extensions/browser/api/messaging/native_message_host.h"
@@ -35,11 +36,16 @@
 
  private:
   void ProcessHello(base::Value response);
+  void ProcessGetRemoteState(base::Value response);
   void ProcessIsUvpaa(const base::Value& request, base::Value response);
 
+  void OnQueryVersionResult(uint32_t version);
   void OnIpcDisconnected();
   void OnIsUvpaaResponse(base::Value response, bool is_available);
 
+  void QueryNextRemoteState();
+  void SendNextRemoteState(bool is_remoted);
+
   // Attempts to connect to the IPC server if the connection has not been
   // established. Returns a boolean indicating whether there is a valid IPC
   // connection to the crd host
@@ -50,6 +56,9 @@
   raw_ptr<extensions::NativeMessageHost::Client> client_ = nullptr;
   ChromotingHostServicesClient host_service_api_client_;
   mojo::Remote<mojom::WebAuthnProxy> remote_;
+
+  // Pending getRemoteStateResponses to be sent.
+  base::queue<base::Value> get_remote_state_responses_;
 };
 
 }  // namespace remoting
diff --git a/services/network/host_resolver.cc b/services/network/host_resolver.cc
index 6ff2139..1829e94 100644
--- a/services/network/host_resolver.cc
+++ b/services/network/host_resolver.cc
@@ -129,8 +129,7 @@
 
 #if defined(OS_ANDROID)
   if (base::FeatureList::IsEnabled(net::features::kRecordRadioWakeupTrigger)) {
-    RadioMonitorAndroid::GetInstance().MaybeRecordResolveHost(
-        optional_parameters);
+    MaybeRecordResolveHostForWakeupTrigger(optional_parameters);
   }
 #endif
 
diff --git a/services/network/host_resolver_unittest.cc b/services/network/host_resolver_unittest.cc
index 1812eab..c5c8c52 100644
--- a/services/network/host_resolver_unittest.cc
+++ b/services/network/host_resolver_unittest.cc
@@ -45,6 +45,7 @@
 #include "base/android/radio_utils.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "net/android/radio_activity_tracker.h"
 #include "net/base/features.h"
 #include "services/network/radio_monitor_android.h"
 #endif
@@ -1589,9 +1590,10 @@
 TEST_F(HostResolverRecordRadioWakeupTest, RecordPreconnect) {
   base::HistogramTester histograms;
 
-  RadioMonitorAndroid::GetInstance().OverrideRadioActivityForTesting(
-      base::android::RadioDataActivity::kDormant);
-  RadioMonitorAndroid::GetInstance().OverrideRadioTypeForTesting(
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideRadioActivityForTesting(
+          base::android::RadioDataActivity::kDormant);
+  net::android::RadioActivityTracker::GetInstance().OverrideRadioTypeForTesting(
       base::android::RadioConnectionType::kCell);
 
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 39f8bd4..4a3f4fe1 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -381,17 +381,25 @@
 void GetCTPolicyConfigForCTLogInfo(
     const std::vector<mojom::CTLogInfoPtr>& log_list,
     std::vector<std::pair<std::string, base::TimeDelta>>* disqualified_logs,
-    std::vector<std::string>* operated_by_google_logs) {
+    std::vector<std::string>* operated_by_google_logs,
+    std::map<std::string, certificate_transparency::OperatorHistoryEntry>*
+        operator_history) {
   for (const auto& log : log_list) {
+    std::string log_id = crypto::SHA256HashString(log->public_key);
     if (log->operated_by_google || log->disqualified_at) {
-      std::string log_id = crypto::SHA256HashString(log->public_key);
       if (log->operated_by_google)
         operated_by_google_logs->push_back(log_id);
       if (log->disqualified_at) {
-        disqualified_logs->emplace_back(std::move(log_id),
-                                        log->disqualified_at.value());
+        disqualified_logs->emplace_back(log_id, log->disqualified_at.value());
       }
     }
+    certificate_transparency::OperatorHistoryEntry entry;
+    entry.current_operator_ = log->current_operator;
+    for (const auto& previous_operator : log->previous_operators) {
+      entry.previous_operators_.emplace_back(previous_operator->name,
+                                             previous_operator->end_time);
+    }
+    (*operator_history)[log_id] = entry;
   }
 
   std::sort(std::begin(*operated_by_google_logs),
@@ -1413,11 +1421,14 @@
     return;
   std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
+  std::map<std::string, certificate_transparency::OperatorHistoryEntry>
+      log_operator_history;
   GetCTPolicyConfigForCTLogInfo(log_list, &disqualified_logs,
-                                &operated_by_google_logs);
-  ct_policy_enforcer_->UpdateCTLogList(update_time,
-                                       std::move(disqualified_logs),
-                                       std::move(operated_by_google_logs));
+                                &operated_by_google_logs,
+                                &log_operator_history);
+  ct_policy_enforcer_->UpdateCTLogList(
+      update_time, std::move(disqualified_logs),
+      std::move(operated_by_google_logs), std::move(log_operator_history));
 }
 #endif  // BUILDFLAG(IS_CT_SUPPORTED)
 
@@ -2133,12 +2144,16 @@
   if (params_->enforce_chrome_ct_policy) {
     std::vector<std::pair<std::string, base::TimeDelta>> disqualified_logs;
     std::vector<std::string> operated_by_google_logs;
+    std::map<std::string, certificate_transparency::OperatorHistoryEntry>
+        log_operator_history;
     GetCTPolicyConfigForCTLogInfo(network_service_->log_list(),
-                                  &disqualified_logs, &operated_by_google_logs);
+                                  &disqualified_logs, &operated_by_google_logs,
+                                  &log_operator_history);
     auto ct_policy_enforcer =
         std::make_unique<certificate_transparency::ChromeCTPolicyEnforcer>(
             network_service_->ct_log_list_update_time(),
-            std::move(disqualified_logs), std::move(operated_by_google_logs));
+            std::move(disqualified_logs), std::move(operated_by_google_logs),
+            std::move(log_operator_history));
     ct_policy_enforcer_ = ct_policy_enforcer.get();
     builder.set_ct_policy_enforcer(std::move(ct_policy_enforcer));
   }
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index fbd2e6f..8ae22bd 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -6145,7 +6145,11 @@
     log_info->public_key = std::string(4, 0x30 + static_cast<char>(i));
     log_info->name = std::string(4, 0x30 + static_cast<char>(i));
     log_info->operated_by_google = i % 2;
-
+    if (log_info->operated_by_google) {
+      log_info->current_operator = "Google";
+    } else {
+      log_info->current_operator = "Not Google";
+    }
     log_list_mojo.push_back(std::move(log_info));
   }
   for (int i = 0; i < 3; ++i) {
@@ -6155,6 +6159,7 @@
     log_info->name = std::string(4, 0x41 + static_cast<char>(i));
     log_info->operated_by_google = false;
     log_info->disqualified_at = base::Seconds(i);
+    log_info->current_operator = "Not Google Either";
 
     log_list_mojo.push_back(std::move(log_info));
   }
@@ -6196,6 +6201,88 @@
           ::testing::Pair(crypto::SHA256HashString("AAAA"), base::Seconds(0)),
           ::testing::Pair(crypto::SHA256HashString("BBBB"), base::Seconds(1)),
           ::testing::Pair(crypto::SHA256HashString("CCCC"), base::Seconds(2))));
+
+  std::map<std::string, certificate_transparency::OperatorHistoryEntry>
+      operator_history = policy_enforcer->operator_history_for_testing();
+
+  for (auto log : policy_enforcer->operated_by_google_logs_for_testing()) {
+    EXPECT_EQ(operator_history[log].current_operator_, "Google");
+    EXPECT_TRUE(operator_history[log].previous_operators_.empty());
+  }
+
+  for (auto log : policy_enforcer->disqualified_logs_for_testing()) {
+    EXPECT_EQ(operator_history[log.first].current_operator_,
+              "Not Google Either");
+    EXPECT_TRUE(operator_history[log.first].previous_operators_.empty());
+  }
+}
+
+TEST_F(NetworkContextTest, CertificateTransparencyConfigWithOperatorSwitches) {
+  // Configure CT logs in network service.
+  std::vector<network::mojom::CTLogInfoPtr> log_list_mojo;
+
+  // The log public keys do not matter for the test, so invalid keys are used.
+  // However, because the log IDs are derived from the SHA-256 hash of the log
+  // key, the log keys are generated such that the log that never switched
+  // operator is "0000", while the one that did is "AAAA".
+  network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New();
+  // Shift to ASCII '0' (0x30)
+  log_info->public_key = std::string(4, 0x30);
+  log_info->name = std::string(4, 0x30);
+  log_info->current_operator = "Forever Operator";
+  log_list_mojo.push_back(std::move(log_info));
+
+  log_info = network::mojom::CTLogInfo::New();
+  // Shift to ASCII 'A' (0x41)
+  log_info->public_key = std::string(4, 0x41);
+  log_info->name = std::string(4, 0x41);
+  log_info->current_operator = "Changed Operator";
+  for (int i = 0; i < 3; i++) {
+    network::mojom::PreviousOperatorEntryPtr previous_operator =
+        network::mojom::PreviousOperatorEntry::New();
+    previous_operator->name = "Operator " + base::NumberToString(i);
+    previous_operator->end_time = base::Seconds(i);
+    log_info->previous_operators.push_back(std::move(previous_operator));
+  }
+  log_list_mojo.push_back(std::move(log_info));
+
+  network_service()->UpdateCtLogList(std::move(log_list_mojo),
+                                     base::Time::Now());
+
+  // Configure CT params in network context.
+  mojom::NetworkContextParamsPtr params =
+      CreateNetworkContextParamsForTesting();
+  params->enforce_chrome_ct_policy = true;
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(std::move(params));
+
+  net::CTPolicyEnforcer* request_enforcer =
+      network_context->url_request_context()->ct_policy_enforcer();
+  ASSERT_TRUE(request_enforcer);
+
+  // Completely unsafe if |enforce_chrome_ct_policy| is false.
+  certificate_transparency::ChromeCTPolicyEnforcer* policy_enforcer =
+      reinterpret_cast<certificate_transparency::ChromeCTPolicyEnforcer*>(
+          request_enforcer);
+
+  std::map<std::string, certificate_transparency::OperatorHistoryEntry>
+      operator_history = policy_enforcer->operator_history_for_testing();
+
+  EXPECT_EQ(
+      operator_history[crypto::SHA256HashString("0000")].current_operator_,
+      "Forever Operator");
+  EXPECT_TRUE(operator_history[crypto::SHA256HashString("0000")]
+                  .previous_operators_.empty());
+
+  EXPECT_EQ(
+      operator_history[crypto::SHA256HashString("AAAA")].current_operator_,
+      "Changed Operator");
+  EXPECT_THAT(
+      operator_history[crypto::SHA256HashString("AAAA")].previous_operators_,
+      ::testing::ElementsAre(::testing::Pair("Operator 0", base::Seconds(0)),
+                             ::testing::Pair("Operator 1", base::Seconds(1)),
+                             ::testing::Pair("Operator 2", base::Seconds(2))));
 }
 #endif
 
diff --git a/services/network/public/mojom/ct_log_info.mojom b/services/network/public/mojom/ct_log_info.mojom
index db0c2d4b..9572c63 100644
--- a/services/network/public/mojom/ct_log_info.mojom
+++ b/services/network/public/mojom/ct_log_info.mojom
@@ -6,6 +6,14 @@
 
 import "mojo/public/mojom/base/time.mojom";
 
+// Structure used to hold information about previous operators for a log.
+struct PreviousOperatorEntry {
+  // Name of the operator.
+  string name;
+  // Time when operator stopped operating this log.
+  mojo_base.mojom.TimeDelta end_time;
+};
+
 // A single Certificate Transparency Log configuration.
 struct CTLogInfo {
   // The DER-encoded SubjectPublicKeyInfo of the log.
@@ -25,4 +33,10 @@
   // is used to determine the "once or currently qualified" status of the log.
   // If the log is currently qualified, this will not be set.
   mojo_base.mojom.TimeDelta? disqualified_at;
+
+  // Current operator for this log.
+  string current_operator;
+
+  // Information about previous operators for this log (if any).
+  array<PreviousOperatorEntry> previous_operators;
 };
diff --git a/services/network/radio_monitor_android.cc b/services/network/radio_monitor_android.cc
index a3439734..b303e2e 100644
--- a/services/network/radio_monitor_android.cc
+++ b/services/network/radio_monitor_android.cc
@@ -6,37 +6,20 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "base/trace_event/trace_event.h"
-#include "net/base/features.h"
+#include "net/android/radio_activity_tracker.h"
 #include "net/base/load_flags.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
 
 namespace network {
 
-namespace {
-
-// The minimum interval for recording possible radio wake-ups. It's unlikely
-// that radio state transitions happen in seconds.
-constexpr static base::TimeDelta
-    kMinimumRecordIntervalForPossibleWakeupTrigger = base::Seconds(1);
-
-}  // namespace
-
-// static
-RadioMonitorAndroid& RadioMonitorAndroid::GetInstance() {
-  static base::NoDestructor<RadioMonitorAndroid> s_instance;
-  return *s_instance;
-}
-
-RadioMonitorAndroid::RadioMonitorAndroid() = default;
-
-void RadioMonitorAndroid::MaybeRecordURLLoader(
+void MaybeRecordURLLoaderCreationForWakeupTrigger(
     const ResourceRequest& request,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
-  DCHECK(
-      base::FeatureList::IsEnabled(net::features::kRecordRadioWakeupTrigger));
-  if (!ShouldRecordRadioWakeupTrigger())
+  if (!net::android::RadioActivityTracker::GetInstance()
+           .ShouldRecordActivityForWakeupTrigger())
     return;
 
   TRACE_EVENT_INSTANT1("loading", "RadioMonitorAndroid::URLLoaderWakeupRadio",
@@ -57,11 +40,10 @@
                            traffic_annotation.unique_id_hash_code);
 }
 
-void RadioMonitorAndroid::MaybeRecordResolveHost(
+void MaybeRecordResolveHostForWakeupTrigger(
     const mojom::ResolveHostParametersPtr& parameters) {
-  DCHECK(
-      base::FeatureList::IsEnabled(net::features::kRecordRadioWakeupTrigger));
-  if (!ShouldRecordRadioWakeupTrigger())
+  if (!net::android::RadioActivityTracker::GetInstance()
+           .ShouldRecordActivityForWakeupTrigger())
     return;
 
   mojom::ResolveHostParameters::Purpose purpose =
@@ -76,55 +58,4 @@
                                 purpose);
 }
 
-bool RadioMonitorAndroid::IsRadioUtilsSupported() {
-  return base::android::RadioUtils::IsSupported() ||
-         radio_activity_override_for_testing_.has_value() ||
-         radio_type_override_for_testing_.has_value();
-}
-
-bool RadioMonitorAndroid::ShouldRecordRadioWakeupTrigger() {
-  if (!IsRadioUtilsSupported())
-    return false;
-
-  base::TimeTicks now = base::TimeTicks::Now();
-  // Check recording interval first to reduce overheads of calling Android's
-  // platform APIs.
-  if (!last_check_time_.is_null() &&
-      now - last_check_time_ < kMinimumRecordIntervalForPossibleWakeupTrigger)
-    return false;
-
-  last_check_time_ = now;
-
-  bool should_record = ShouldRecordRadioWakeupTriggerInternal();
-  base::UmaHistogramTimes(
-      "Network.Radio.PossibleWakeupTrigger.RadioUtilsOverhead",
-      base::TimeTicks::Now() - now);
-
-  return should_record;
-}
-
-bool RadioMonitorAndroid::ShouldRecordRadioWakeupTriggerInternal() {
-  base::android::RadioConnectionType radio_type =
-      radio_type_override_for_testing_.value_or(
-          base::android::RadioUtils::GetConnectionType());
-  if (radio_type != base::android::RadioConnectionType::kCell)
-    return false;
-
-  absl::optional<base::android::RadioDataActivity> radio_activity =
-      radio_activity_override_for_testing_.has_value()
-          ? radio_activity_override_for_testing_
-          : base::android::RadioUtils::GetCellDataActivity();
-
-  if (!radio_activity.has_value())
-    return false;
-
-  // When the last activity was dormant, don't treat this event as a wakeup
-  // trigger since there could be state transition delay and startup latency.
-  bool should_record =
-      *radio_activity == base::android::RadioDataActivity::kDormant &&
-      last_radio_data_activity_ != base::android::RadioDataActivity::kDormant;
-  last_radio_data_activity_ = *radio_activity;
-  return should_record;
-}
-
 }  // namespace network
diff --git a/services/network/radio_monitor_android.h b/services/network/radio_monitor_android.h
index a3a08140..bea3a77 100644
--- a/services/network/radio_monitor_android.h
+++ b/services/network/radio_monitor_android.h
@@ -5,13 +5,12 @@
 #ifndef SERVICES_NETWORK_RADIO_MONITOR_ANDROID_H_
 #define SERVICES_NETWORK_RADIO_MONITOR_ANDROID_H_
 
-#include "base/android/radio_utils.h"
 #include "base/component_export.h"
-#include "base/no_destructor.h"
-#include "base/time/time.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/mojom/host_resolver.mojom-forward.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace net {
+struct NetworkTrafficAnnotationTag;
+}  // namespace net
 
 namespace network {
 
@@ -28,68 +27,17 @@
 constexpr char kUmaNamePossibleWakeupTriggerResolveHost[] =
     "Network.Radio.PossibleWakeupTrigger.ResolveHostPurpose2";
 
-// Checks radio states and records histograms when network activities may
-// trigger power-consuming radio state changes like wake-ups.
-class COMPONENT_EXPORT(NETWORK_SERVICE) RadioMonitorAndroid {
- public:
-  static RadioMonitorAndroid& GetInstance();
+// Records UMAs when a network request initiated by a URLLoader likely
+// wake-ups radio.
+COMPONENT_EXPORT(NETWORK_SERVICE)
+void MaybeRecordURLLoaderCreationForWakeupTrigger(
+    const ResourceRequest& request,
+    const net::NetworkTrafficAnnotationTag& traffic_annotation);
 
-  RadioMonitorAndroid(const RadioMonitorAndroid&) = delete;
-  RadioMonitorAndroid& operator=(const RadioMonitorAndroid&) = delete;
-  RadioMonitorAndroid(RadioMonitorAndroid&&) = delete;
-  RadioMonitorAndroid& operator=(RadioMonitorAndroid&&) = delete;
-
-  // Records UMAs when a network request initiated by a URLLoader likely
-  // wake-ups radio.
-  void MaybeRecordURLLoader(
-      const ResourceRequest& request,
-      const net::NetworkTrafficAnnotationTag& traffic_annotation);
-
-  // Records a host resolve request when the request likely wake-ups radio.
-  void MaybeRecordResolveHost(
-      const mojom::ResolveHostParametersPtr& parameters);
-
-  // These override internal members for testing.
-  void OverrideRadioActivityForTesting(
-      absl::optional<base::android::RadioDataActivity> radio_activity) {
-    radio_activity_override_for_testing_ = radio_activity;
-  }
-  void OverrideRadioTypeForTesting(
-      absl::optional<base::android::RadioConnectionType> radio_type) {
-    radio_type_override_for_testing_ = radio_type;
-  }
-  void OverrideLastCheckTimeForTesting(base::TimeTicks last_check_time) {
-    last_check_time_ = last_check_time;
-  }
-
- private:
-  friend class base::NoDestructor<RadioMonitorAndroid>;
-  RadioMonitorAndroid();
-  ~RadioMonitorAndroid() = delete;
-
-  // Returns true when RadioUtils is available or any radio states are
-  // overridden for testing.
-  bool IsRadioUtilsSupported();
-
-  // Returns true when an incoming network event such as creating URLLoader and
-  // resolving a host name could trigger a radio wakeup.
-  // TODO(crbug.com/1232623): Consider optimizing this function. This function
-  // uses Android's platform APIs which add non-negligible overheads.
-  bool ShouldRecordRadioWakeupTrigger();
-  // Contains maybe-expensive API calls.
-  bool ShouldRecordRadioWakeupTriggerInternal();
-
-  // Updated when ShouldRecordRadioWakeupTrigger() is called.
-  base::android::RadioDataActivity last_radio_data_activity_ =
-      base::android::RadioDataActivity::kNone;
-  base::TimeTicks last_check_time_;
-
-  // Radio state overrides for testing.
-  absl::optional<base::android::RadioDataActivity>
-      radio_activity_override_for_testing_;
-  absl::optional<base::android::RadioConnectionType>
-      radio_type_override_for_testing_;
-};
+// Records a host resolve request when the request likely wake-ups radio.
+COMPONENT_EXPORT(NETWORK_SERVICE)
+void MaybeRecordResolveHostForWakeupTrigger(
+    const mojom::ResolveHostParametersPtr& parameters);
 
 }  // namespace network
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 138ec0d..804ecca 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -712,8 +712,7 @@
 
 #if defined(OS_ANDROID)
   if (base::FeatureList::IsEnabled(net::features::kRecordRadioWakeupTrigger)) {
-    RadioMonitorAndroid::GetInstance().MaybeRecordURLLoader(request,
-                                                            traffic_annotation);
+    MaybeRecordURLLoaderCreationForWakeupTrigger(request, traffic_annotation);
   }
 #endif
 
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 96e2b7d7..af0e149 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -112,6 +112,7 @@
 
 #if defined(OS_ANDROID)
 #include "base/android/radio_utils.h"
+#include "net/android/radio_activity_tracker.h"
 #include "services/network/radio_monitor_android.h"
 #endif
 
@@ -7299,9 +7300,10 @@
 TEST_F(URLLoaderTest, RecordRadioWakeupTrigger_Record) {
   base::HistogramTester histograms;
 
-  RadioMonitorAndroid::GetInstance().OverrideRadioActivityForTesting(
-      base::android::RadioDataActivity::kDormant);
-  RadioMonitorAndroid::GetInstance().OverrideRadioTypeForTesting(
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideRadioActivityForTesting(
+          base::android::RadioDataActivity::kDormant);
+  net::android::RadioActivityTracker::GetInstance().OverrideRadioTypeForTesting(
       base::android::RadioConnectionType::kCell);
 
   LoadAndCompareFile("simple_page.html");
@@ -7312,9 +7314,10 @@
 TEST_F(URLLoaderTest, RecordRadioWakeupTrigger_RadioTypeIsNotCell) {
   base::HistogramTester histograms;
 
-  RadioMonitorAndroid::GetInstance().OverrideRadioActivityForTesting(
-      base::android::RadioDataActivity::kDormant);
-  RadioMonitorAndroid::GetInstance().OverrideRadioTypeForTesting(
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideRadioActivityForTesting(
+          base::android::RadioDataActivity::kDormant);
+  net::android::RadioActivityTracker::GetInstance().OverrideRadioTypeForTesting(
       base::android::RadioConnectionType::kWifi);
 
   LoadAndCompareFile("simple_page.html");
@@ -7325,9 +7328,10 @@
 TEST_F(URLLoaderTest, RecordRadioWakeupTrigger_RadioActivityIsNotDormant) {
   base::HistogramTester histograms;
 
-  RadioMonitorAndroid::GetInstance().OverrideRadioActivityForTesting(
-      base::android::RadioDataActivity::kInOut);
-  RadioMonitorAndroid::GetInstance().OverrideRadioTypeForTesting(
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideRadioActivityForTesting(
+          base::android::RadioDataActivity::kInOut);
+  net::android::RadioActivityTracker::GetInstance().OverrideRadioTypeForTesting(
       base::android::RadioConnectionType::kCell);
 
   LoadAndCompareFile("simple_page.html");
@@ -7338,12 +7342,13 @@
 TEST_F(URLLoaderTest, RecordRadioWakeupTrigger_IntervalTooShort) {
   base::HistogramTester histograms;
 
-  RadioMonitorAndroid::GetInstance().OverrideRadioActivityForTesting(
-      base::android::RadioDataActivity::kDormant);
-  RadioMonitorAndroid::GetInstance().OverrideRadioTypeForTesting(
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideRadioActivityForTesting(
+          base::android::RadioDataActivity::kDormant);
+  net::android::RadioActivityTracker::GetInstance().OverrideRadioTypeForTesting(
       base::android::RadioConnectionType::kCell);
-  RadioMonitorAndroid::GetInstance().OverrideLastCheckTimeForTesting(
-      base::TimeTicks::Now());
+  net::android::RadioActivityTracker::GetInstance()
+      .OverrideLastCheckTimeForTesting(base::TimeTicks::Now());
 
   LoadAndCompareFile("simple_page.html");
 
diff --git a/services/preferences/public/cpp/dictionary_value_update.cc b/services/preferences/public/cpp/dictionary_value_update.cc
index ec65bdf..b3d6378 100644
--- a/services/preferences/public/cpp/dictionary_value_update.cc
+++ b/services/preferences/public/cpp/dictionary_value_update.cc
@@ -42,7 +42,7 @@
     return;
 
   RecordSplitPath(std::vector<base::StringPiece>());
-  value_->Clear();
+  value_->DictClear();
 }
 
 void DictionaryValueUpdate::Set(base::StringPiece path,
diff --git a/services/shape_detection/BUILD.gn b/services/shape_detection/BUILD.gn
index 11574ebb..47c5677 100644
--- a/services/shape_detection/BUILD.gn
+++ b/services/shape_detection/BUILD.gn
@@ -12,16 +12,16 @@
     "text_detection_impl.h",
   ]
 
-  deps = [
-    "//build:branding_buildflags",
-    "//build:chromeos_buildflags",
-    "//mojo/public/cpp/bindings",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-  ]
-
   if (is_mac) {
     sources += [
+      "barcode_detection_impl_mac.h",
+      "barcode_detection_impl_mac.mm",
+      "barcode_detection_impl_mac_vision.h",
+      "barcode_detection_impl_mac_vision.mm",
+      "barcode_detection_impl_mac_vision_api.h",
+      "barcode_detection_impl_mac_vision_api.mm",
+      "barcode_detection_provider_mac.h",
+      "barcode_detection_provider_mac.mm",
       "detection_utils_mac.h",
       "detection_utils_mac.mm",
       "face_detection_impl_mac.h",
@@ -37,6 +37,8 @@
     weak_frameworks = [ "Vision.framework" ]
   } else if (is_win) {
     sources += [
+      "barcode_detection_provider_impl.cc",
+      "barcode_detection_provider_impl.h",
       "detection_utils_win.cc",
       "detection_utils_win.h",
       "face_detection_impl_win.cc",
@@ -46,53 +48,40 @@
       "text_detection_impl_win.cc",
       "text_detection_impl_win.h",
     ]
-  } else if (is_android) {
-    # No C++ sources needed, face and text detection is provided by Java.
+  } else if (is_chromeos_ash && is_chrome_branded) {
+    sources += [
+      "barcode_detection_impl_barhopper.cc",
+      "barcode_detection_impl_barhopper.h",
+      "barcode_detection_provider_barhopper.cc",
+      "barcode_detection_provider_barhopper.h",
+      "face_detection_provider_impl.cc",
+      "face_detection_provider_impl.h",
+      "text_detection_impl.cc",
+    ]
   } else {
     sources += [
+      "barcode_detection_provider_impl.cc",
+      "barcode_detection_provider_impl.h",
       "face_detection_provider_impl.cc",
       "face_detection_provider_impl.h",
       "text_detection_impl.cc",
     ]
   }
 
-  if (is_mac) {
-    # On macOS there is a barcode detection API available from the platform.
-    sources += [
-      "barcode_detection_impl_mac.h",
-      "barcode_detection_impl_mac.mm",
-      "barcode_detection_impl_mac_vision.h",
-      "barcode_detection_impl_mac_vision.mm",
-      "barcode_detection_impl_mac_vision_api.h",
-      "barcode_detection_impl_mac_vision_api.mm",
-      "barcode_detection_provider_mac.h",
-      "barcode_detection_provider_mac.mm",
-    ]
-  } else if (is_android) {
-    # No C++ sources needed, barcode detection is provided by Java.
-  } else if (!is_fuchsia && is_chrome_branded) {
-    # On all other platforms (except Fuchsia) use Google's "Barhopper" library
-    # if src-internal is available.
-    #
-    # For the moment this library is not built on Fuchsia in order to reduce
-    # binary size. See https://crbug.com/1275298.
-    sources += [
-      "barcode_detection_impl_barhopper.cc",
-      "barcode_detection_impl_barhopper.h",
-      "barcode_detection_provider_barhopper.cc",
-      "barcode_detection_provider_barhopper.h",
-    ]
-    deps += [ "//third_party/barhopper" ]
-  } else {
-    # Otherwise, use a stub implementation.
-    sources += [
-      "barcode_detection_provider_impl.cc",
-      "barcode_detection_provider_impl.h",
-    ]
-  }
-
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
+  deps = [
+    "//build:branding_buildflags",
+    "//build:chromeos_buildflags",
+    "//mojo/public/cpp/bindings",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  if (is_chromeos_ash && is_chrome_branded) {
+    deps += [ "//third_party/barhopper:barhopper" ]
+  }
+
   public_deps = [
     "//base",
     "//media/capture",
@@ -164,7 +153,7 @@
     ]
   }
 
-  if (!is_mac && !is_android && !is_fuchsia && is_chrome_branded) {
+  if (is_chromeos_ash && is_chrome_branded) {
     sources += [ "barcode_detection_impl_barhopper_unittest.cc" ]
   }
 
diff --git a/services/shape_detection/barcode_detection_impl_barhopper_unittest.cc b/services/shape_detection/barcode_detection_impl_barhopper_unittest.cc
index 644cd68..d934ca7 100644
--- a/services/shape_detection/barcode_detection_impl_barhopper_unittest.cc
+++ b/services/shape_detection/barcode_detection_impl_barhopper_unittest.cc
@@ -15,27 +15,25 @@
 
 namespace shape_detection {
 
-constexpr struct TestParams {
-  base::FilePath::StringPieceType filename;
-  base::StringPiece expected_value;
+struct TestParams {
+  std::string filename;
+  std::string expected_value;
   float x;
   float y;
   float width;
   float height;
-} kTestParams[] = {
-    {FILE_PATH_LITERAL("codabar.png"), "A6.2831853B", 24, 24, 448, 95},
-    {FILE_PATH_LITERAL("code_39.png"), "CHROMIUM", 20, 20, 318, 75},
-    {FILE_PATH_LITERAL("code_93.png"), "CHROMIUM", 20, 20, 216, 75},
-    {FILE_PATH_LITERAL("code_128.png"), "Chromium", 20, 20, 246, 75},
-    {FILE_PATH_LITERAL("data_matrix.png"), "Chromium", 11, 11, 53, 53},
-    {FILE_PATH_LITERAL("ean_8.png"), "62831857", 14, 10, 134, 75},
-    {FILE_PATH_LITERAL("ean_13.png"), "6283185307179", 27, 10, 190, 75},
-    {FILE_PATH_LITERAL("itf.png"), "62831853071795", 10, 10, 135, 39},
-    {FILE_PATH_LITERAL("pdf417.png"), "Chromium", 20, 20, 240, 44},
-    {FILE_PATH_LITERAL("qr_code.png"), "https://chromium.org", 40, 40, 250,
-     250},
-    {FILE_PATH_LITERAL("upc_a.png"), "628318530714", 23, 10, 190, 75},
-    {FILE_PATH_LITERAL("upc_e.png"), "06283186", 23, 10, 102, 75}};
+} kTestParams[] = {{"codabar.png", "A6.2831853B", 24, 24, 448, 95},
+                   {"code_39.png", "CHROMIUM", 20, 20, 318, 75},
+                   {"code_93.png", "CHROMIUM", 20, 20, 216, 75},
+                   {"code_128.png", "Chromium", 20, 20, 246, 75},
+                   {"data_matrix.png", "Chromium", 11, 11, 53, 53},
+                   {"ean_8.png", "62831857", 14, 10, 134, 75},
+                   {"ean_13.png", "6283185307179", 27, 10, 190, 75},
+                   {"itf.png", "62831853071795", 10, 10, 135, 39},
+                   {"pdf417.png", "Chromium", 20, 20, 240, 44},
+                   {"qr_code.png", "https://chromium.org", 40, 40, 250, 250},
+                   {"upc_a.png", "628318530714", 23, 10, 190, 75},
+                   {"upc_e.png", "06283186", 23, 10, 102, 75}};
 
 class BarcodeDetectionImplBarhopperTest
     : public testing::TestWithParam<struct TestParams> {
@@ -60,15 +58,14 @@
     return barcode_service;
   }
 
-  std::unique_ptr<SkBitmap> LoadTestImage(
-      base::FilePath::StringPieceType filename) {
+  std::unique_ptr<SkBitmap> LoadTestImage(std::string filename) {
     // Load image data from test directory.
     base::FilePath image_path;
     EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &image_path));
     image_path = image_path.Append(FILE_PATH_LITERAL("services"))
                      .Append(FILE_PATH_LITERAL("test"))
                      .Append(FILE_PATH_LITERAL("data"))
-                     .Append(filename);
+                     .Append(FILE_PATH_LITERAL(filename));
     EXPECT_TRUE(base::PathExists(image_path));
     std::string image_data;
     EXPECT_TRUE(base::ReadFileToString(image_path, &image_data));
@@ -117,4 +114,4 @@
                          BarcodeDetectionImplBarhopperTest,
                          testing::ValuesIn(kTestParams));
 
-}  // namespace shape_detection
+}  // namespace shape_detection
\ No newline at end of file
diff --git a/services/shape_detection/shape_detection_service.cc b/services/shape_detection/shape_detection_service.cc
index 9bc5c92..542c266f 100644
--- a/services/shape_detection/shape_detection_service.cc
+++ b/services/shape_detection/shape_detection_service.cc
@@ -10,6 +10,19 @@
 #include "base/bind.h"
 #include "build/branding_buildflags.h"
 #include "build/chromeos_buildflags.h"
+#if defined(OS_WIN)
+#include "services/shape_detection/barcode_detection_provider_impl.h"
+#include "services/shape_detection/face_detection_provider_win.h"
+#elif defined(OS_MAC)
+#include "services/shape_detection/barcode_detection_provider_mac.h"
+#include "services/shape_detection/face_detection_provider_mac.h"
+#elif BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_CHROMEOS_ASH)
+#include "services/shape_detection/barcode_detection_provider_barhopper.h"
+#include "services/shape_detection/face_detection_provider_impl.h"
+#else
+#include "services/shape_detection/barcode_detection_provider_impl.h"
+#include "services/shape_detection/face_detection_provider_impl.h"
+#endif
 #include "services/shape_detection/text_detection_impl.h"
 
 #if defined(OS_ANDROID)
@@ -17,26 +30,6 @@
 #include "services/shape_detection/shape_detection_jni_headers/InterfaceRegistrar_jni.h"
 #endif
 
-#if defined(OS_MAC)
-#include "services/shape_detection/barcode_detection_provider_mac.h"
-#elif defined(OS_ANDROID)
-// Barcode detection comes from Java.
-#elif !defined(OS_FUCHSIA) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-#include "services/shape_detection/barcode_detection_provider_barhopper.h"
-#else
-#include "services/shape_detection/barcode_detection_provider_impl.h"
-#endif
-
-#if defined(OS_WIN)
-#include "services/shape_detection/face_detection_provider_win.h"
-#elif defined(OS_MAC)
-#include "services/shape_detection/face_detection_provider_mac.h"
-#elif defined(OS_ANDROID)
-// Face detection comes from Java.
-#else
-#include "services/shape_detection/face_detection_provider_impl.h"
-#endif
-
 namespace shape_detection {
 
 ShapeDetectionService::ShapeDetectionService(
@@ -54,7 +47,7 @@
       receiver.PassPipe().release().value());
 #elif defined(OS_MAC)
   BarcodeDetectionProviderMac::Create(std::move(receiver));
-#elif !defined(OS_FUCHSIA) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#elif BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_CHROMEOS_ASH)
   BarcodeDetectionProviderBarhopper::Create(std::move(receiver));
 #else
   BarcodeDetectionProviderImpl::Create(std::move(receiver));
diff --git a/storage/browser/file_system/copy_or_move_operation_delegate.cc b/storage/browser/file_system/copy_or_move_operation_delegate.cc
index b3d3763..05a6108 100644
--- a/storage/browser/file_system/copy_or_move_operation_delegate.cc
+++ b/storage/browser/file_system/copy_or_move_operation_delegate.cc
@@ -39,6 +39,10 @@
   virtual void Run(CopyOrMoveOperationDelegate::StatusCallback callback) = 0;
   virtual void Cancel() = 0;
 
+  // Force any file copy to result in an error. This affects copies or
+  // cross-filesystem moves.
+  void ForceCopyErrorForTest() { force_error_for_test_ = true; }
+
  protected:
   CopyOrMoveImpl(
       FileSystemOperationRunner* operation_runner,
@@ -158,6 +162,7 @@
   const FileSystemURL src_url_;
   const FileSystemURL dest_url_;
   const CopyOrMoveOperationDelegate::CopyOrMoveOptionSet options_;
+  bool force_error_for_test_ = false;
 
  private:
   const FileSystemOperation::CopyOrMoveProgressCallback progress_callback_;
@@ -363,6 +368,9 @@
   void RunAfterPostWriteValidation(
       CopyOrMoveOperationDelegate::StatusCallback callback,
       base::File::Error error) {
+    if (force_error_for_test_) {
+      error = base::File::FILE_ERROR_FAILED;
+    }
     if (cancel_requested_) {
       DidEndCopy(std::move(callback), base::File::FILE_ERROR_ABORT);
       return;
@@ -646,6 +654,9 @@
 
     NotifyOnModifyFile(dest_url_);
     NotifyOnEndUpdate(dest_url_);
+    if (force_error_for_test_) {
+      error = base::File::FILE_ERROR_FAILED;
+    }
     if (cancel_requested_)
       error = base::File::FILE_ERROR_ABORT;
 
@@ -963,9 +974,11 @@
   }
 
   // Register the running task.
-
   CopyOrMoveImpl* impl_ptr = impl.get();
   running_copy_set_[impl_ptr] = std::move(impl);
+  if (src_url == error_url_for_test_) {
+    impl_ptr->ForceCopyErrorForTest();  // IN-TEST
+  }
   impl_ptr->Run(base::BindOnce(&CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
                                weak_factory_.GetWeakPtr(), std::move(callback),
                                impl_ptr));
diff --git a/storage/browser/file_system/copy_or_move_operation_delegate.h b/storage/browser/file_system/copy_or_move_operation_delegate.h
index 67d8dbc..ec3a3db 100644
--- a/storage/browser/file_system/copy_or_move_operation_delegate.h
+++ b/storage/browser/file_system/copy_or_move_operation_delegate.h
@@ -27,7 +27,8 @@
 enum class FlushPolicy;
 
 // A delegate class for recursive copy or move operations.
-class CopyOrMoveOperationDelegate : public RecursiveOperationDelegate {
+class COMPONENT_EXPORT(STORAGE_BROWSER) CopyOrMoveOperationDelegate
+    : public RecursiveOperationDelegate {
  public:
   class CopyOrMoveImpl;
   using CopyOrMoveProgressCallback =
@@ -111,6 +112,11 @@
                         StatusCallback callback) override;
   void PostProcessDirectory(const FileSystemURL& url,
                             StatusCallback callback) override;
+  // Force a given source URL to produce an error for a copy or a
+  // cross-filesystem move.
+  void SetErrorUrlForTest(const FileSystemURL& url) {
+    error_url_for_test_ = url;
+  }
 
  protected:
   void OnCancel() override;
@@ -152,6 +158,7 @@
   const ErrorBehavior error_behavior_;
   const CopyOrMoveProgressCallback progress_callback_;
   StatusCallback callback_;
+  FileSystemURL error_url_for_test_;
 
   std::map<CopyOrMoveImpl*, std::unique_ptr<CopyOrMoveImpl>> running_copy_set_;
   base::WeakPtrFactory<CopyOrMoveOperationDelegate> weak_factory_{this};
diff --git a/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc b/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc
index bbc9e48..a5c5349 100644
--- a/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc
+++ b/storage/browser/file_system/copy_or_move_operation_delegate_unittest.cc
@@ -53,6 +53,8 @@
 
 namespace {
 
+constexpr int64_t kDefaultFileSize = 10;
+
 void ExpectOk(const GURL& origin_url,
               const std::string& name,
               base::File::Error error) {
@@ -1023,4 +1025,259 @@
   EXPECT_EQ(base::File::FILE_ERROR_ABORT, error);
 }
 
+class CopyOrMoveOperationDelegateTestHelper {
+ public:
+  CopyOrMoveOperationDelegateTestHelper(
+      const std::string& origin,
+      FileSystemType src_type,
+      FileSystemType dest_type,
+      FileSystemOperation::CopyOrMoveOptionSet options)
+      : origin_(url::Origin::Create(GURL(origin))),
+        src_type_(src_type),
+        dest_type_(dest_type),
+        options_(options),
+        task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
+
+  CopyOrMoveOperationDelegateTestHelper(
+      const CopyOrMoveOperationDelegateTestHelper&) = delete;
+  CopyOrMoveOperationDelegateTestHelper& operator=(
+      const CopyOrMoveOperationDelegateTestHelper&) = delete;
+
+  ~CopyOrMoveOperationDelegateTestHelper() {
+    file_system_context_ = nullptr;
+    task_environment_.RunUntilIdle();
+  }
+
+  void SetUp() {
+    ASSERT_TRUE(base_.CreateUniqueTempDir());
+    base::FilePath base_dir = base_.GetPath();
+    file_system_context_ =
+        storage::CreateFileSystemContextForTesting(nullptr, base_dir);
+
+    // Prepare the origin's root directory.
+    FileSystemBackend* backend =
+        file_system_context_->GetFileSystemBackend(src_type_);
+    backend->ResolveURL(
+        FileSystemURL::CreateForTest(blink::StorageKey(url::Origin(origin_)),
+                                     src_type_, base::FilePath()),
+        OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, base::BindOnce(&ExpectOk));
+    backend = file_system_context_->GetFileSystemBackend(dest_type_);
+    backend->ResolveURL(
+        FileSystemURL::CreateForTest(blink::StorageKey(url::Origin(origin_)),
+                                     dest_type_, base::FilePath()),
+        OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, base::BindOnce(&ExpectOk));
+    task_environment_.RunUntilIdle();
+  }
+
+  FileSystemURL GenerateSourceUrlFromPath(const std::string& path) {
+    return file_system_context_->CreateCrackedFileSystemURL(
+        blink::StorageKey(origin_), src_type_,
+        base::FilePath::FromUTF8Unsafe(path));
+  }
+
+  FileSystemURL GenerateDestinationUrlFromPath(const std::string& path) {
+    return file_system_context_->CreateCrackedFileSystemURL(
+        blink::StorageKey(origin_), dest_type_,
+        base::FilePath::FromUTF8Unsafe(path));
+  }
+
+  base::File::Error CreateFile(const FileSystemURL& url, size_t size) {
+    base::File::Error result =
+        AsyncFileTestHelper::CreateFile(file_system_context_.get(), url);
+    if (result != base::File::FILE_OK)
+      return result;
+    return AsyncFileTestHelper::TruncateFile(file_system_context_.get(), url,
+                                             size);
+  }
+
+  base::File::Error CreateDirectory(const FileSystemURL& url) {
+    return AsyncFileTestHelper::CreateDirectory(file_system_context_.get(),
+                                                url);
+  }
+
+  bool FileExists(const FileSystemURL& url, int64_t expected_size) {
+    return AsyncFileTestHelper::FileExists(file_system_context_.get(), url,
+                                           expected_size);
+  }
+
+  bool DirectoryExists(const FileSystemURL& url) {
+    return AsyncFileTestHelper::DirectoryExists(file_system_context_.get(),
+                                                url);
+  }
+
+  // Force Copy or Move error when a given URL is encountered.
+  void SetErrorUrl(const FileSystemURL& url) { error_url_ = url; }
+
+  base::File::Error Copy(const FileSystemURL& src, const FileSystemURL& dest) {
+    base::RunLoop run_loop;
+    base::File::Error result = base::File::FILE_ERROR_FAILED;
+
+    CopyOrMoveOperationDelegate copy_or_move_operation_delegate(
+        file_system_context_.get(), src, dest,
+        CopyOrMoveOperationDelegate::OPERATION_COPY, options_,
+        FileSystemOperation::ERROR_BEHAVIOR_ABORT,
+        FileSystemOperation::CopyOrMoveProgressCallback(),
+        base::BindOnce(&AssignAndQuit, &run_loop, base::Unretained(&result)));
+    if (error_url_.is_valid()) {
+      copy_or_move_operation_delegate.SetErrorUrlForTest(error_url_);
+    }
+    copy_or_move_operation_delegate.RunRecursively();
+    run_loop.Run();
+    return result;
+  }
+
+  base::File::Error Move(const FileSystemURL& src, const FileSystemURL& dest) {
+    base::RunLoop run_loop;
+    base::File::Error result = base::File::FILE_ERROR_FAILED;
+
+    CopyOrMoveOperationDelegate copy_or_move_operation_delegate(
+        file_system_context_.get(), src, dest,
+        CopyOrMoveOperationDelegate::OPERATION_MOVE, options_,
+        FileSystemOperation::ERROR_BEHAVIOR_ABORT,
+        FileSystemOperation::CopyOrMoveProgressCallback(),
+        base::BindOnce(&AssignAndQuit, &run_loop, base::Unretained(&result)));
+    if (error_url_.is_valid()) {
+      copy_or_move_operation_delegate.SetErrorUrlForTest(error_url_);
+    }
+    copy_or_move_operation_delegate.RunRecursively();
+    run_loop.Run();
+    return result;
+  }
+
+ private:
+  base::ScopedTempDir base_;
+
+  const url::Origin origin_;
+  const FileSystemType src_type_;
+  const FileSystemType dest_type_;
+  FileSystemOperation::CopyOrMoveOptionSet options_;
+
+  FileSystemURL error_url_;
+
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<FileSystemContext> file_system_context_;
+};
+
+TEST(CopyOrMoveOperationDelegateTest, StopRecursionOnCopyError) {
+  FileSystemOperation::CopyOrMoveOptionSet options;
+  CopyOrMoveOperationDelegateTestHelper helper(
+      "http://foo", kFileSystemTypePersistent, kFileSystemTypePersistent,
+      options);
+  helper.SetUp();
+
+  FileSystemURL src = helper.GenerateSourceUrlFromPath("a");
+  FileSystemURL src_file_1 = helper.GenerateSourceUrlFromPath("a/file 1");
+  FileSystemURL src_file_2 = helper.GenerateSourceUrlFromPath("a/file 2");
+  FileSystemURL dest = helper.GenerateDestinationUrlFromPath("b");
+  FileSystemURL dest_file_1 = helper.GenerateDestinationUrlFromPath("b/file 1");
+  FileSystemURL dest_file_2 = helper.GenerateDestinationUrlFromPath("b/file 2");
+
+  // Set up source files.
+  ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_1, kDefaultFileSize));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_2, kDefaultFileSize));
+
+  // [file 1, file 2] are processed as a LIFO. An error is returned after
+  // copying file 2.
+  helper.SetErrorUrl(src_file_2);
+  ASSERT_EQ(base::File::FILE_ERROR_FAILED, helper.Copy(src, dest));
+
+  EXPECT_TRUE(helper.DirectoryExists(src));
+  EXPECT_TRUE(helper.DirectoryExists(dest));
+  // Check: file 2 is copied, even though the copy results in an error.
+  EXPECT_TRUE(helper.FileExists(src_file_2, kDefaultFileSize));
+  EXPECT_TRUE(
+      helper.FileExists(dest_file_2, AsyncFileTestHelper::kDontCheckSize));
+  // Check: the recursion has been interrupted after the error, so file 1 hasn't
+  // been copied.
+  EXPECT_TRUE(helper.FileExists(src_file_1, kDefaultFileSize));
+  EXPECT_FALSE(
+      helper.FileExists(dest_file_1, AsyncFileTestHelper::kDontCheckSize));
+}
+
+TEST(CopyOrMoveOperationDelegateTest, RemoveDestFileOnCopyError) {
+  FileSystemOperation::CopyOrMoveOptionSet options(
+      storage::FileSystemOperation::CopyOrMoveOption::
+          kRemovePartiallyCopiedFilesOnError);
+  CopyOrMoveOperationDelegateTestHelper helper(
+      "http://foo", kFileSystemTypePersistent, kFileSystemTypePersistent,
+      options);
+  helper.SetUp();
+
+  FileSystemURL src = helper.GenerateSourceUrlFromPath("a");
+  FileSystemURL src_file_1 = helper.GenerateSourceUrlFromPath("a/file 1");
+  FileSystemURL src_file_2 = helper.GenerateSourceUrlFromPath("a/file 2");
+  FileSystemURL dest = helper.GenerateDestinationUrlFromPath("b");
+  FileSystemURL dest_file_1 = helper.GenerateDestinationUrlFromPath("b/file 1");
+  FileSystemURL dest_file_2 = helper.GenerateDestinationUrlFromPath("b/file 2");
+
+  // Set up source files.
+  ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_1, kDefaultFileSize));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_2, kDefaultFileSize));
+
+  // [file 1, file 2] are processed as a LIFO. An error is returned after
+  // copying file 1.
+  helper.SetErrorUrl(src_file_1);
+  ASSERT_EQ(base::File::FILE_ERROR_FAILED, helper.Copy(src, dest));
+
+  EXPECT_TRUE(helper.DirectoryExists(src));
+  EXPECT_TRUE(helper.DirectoryExists(dest));
+  // Check: file 2 is properly copied.
+  EXPECT_TRUE(helper.FileExists(src_file_2, kDefaultFileSize));
+  EXPECT_TRUE(helper.FileExists(dest_file_2, kDefaultFileSize));
+  // Check: file 1 has been removed on error after being copied.
+  EXPECT_TRUE(helper.FileExists(src_file_1, kDefaultFileSize));
+  EXPECT_FALSE(
+      helper.FileExists(dest_file_1, AsyncFileTestHelper::kDontCheckSize));
+}
+
+TEST(CopyOrMoveOperationDelegateTest,
+     RemoveDestFileOnCrossFilesystemMoveError) {
+  FileSystemOperation::CopyOrMoveOptionSet options(
+      storage::FileSystemOperation::CopyOrMoveOption::
+          kRemovePartiallyCopiedFilesOnError);
+  // Removing destination files on Move errors applies only to cross-filesystem
+  // moves.
+  CopyOrMoveOperationDelegateTestHelper helper(
+      "http://foo", kFileSystemTypeTemporary, kFileSystemTypePersistent,
+      options);
+  helper.SetUp();
+
+  FileSystemURL src = helper.GenerateSourceUrlFromPath("a");
+  FileSystemURL src_file_1 = helper.GenerateSourceUrlFromPath("a/file 1");
+  FileSystemURL src_file_2 = helper.GenerateSourceUrlFromPath("a/file 2");
+  FileSystemURL dest = helper.GenerateDestinationUrlFromPath("b");
+  FileSystemURL dest_file_1 = helper.GenerateDestinationUrlFromPath("b/file 1");
+  FileSystemURL dest_file_2 = helper.GenerateDestinationUrlFromPath("b/file 2");
+
+  // Set up source files.
+  ASSERT_EQ(base::File::FILE_OK, helper.CreateDirectory(src));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_1, kDefaultFileSize));
+  ASSERT_EQ(base::File::FILE_OK,
+            helper.CreateFile(src_file_2, kDefaultFileSize));
+
+  // [file 1, file 2] are processed as a LIFO. An error is returned after
+  // copying file 1.
+  helper.SetErrorUrl(src_file_1);
+  ASSERT_EQ(base::File::FILE_ERROR_FAILED, helper.Move(src, dest));
+
+  EXPECT_TRUE(helper.DirectoryExists(src));
+  EXPECT_TRUE(helper.DirectoryExists(dest));
+  // Check: file 2 is moved.
+  EXPECT_FALSE(
+      helper.FileExists(src_file_2, AsyncFileTestHelper::kDontCheckSize));
+  EXPECT_TRUE(helper.FileExists(dest_file_2, kDefaultFileSize));
+  // Check: destination file 1 has been removed on error, and its source still
+  // exists.
+  EXPECT_TRUE(helper.FileExists(src_file_1, kDefaultFileSize));
+  EXPECT_FALSE(
+      helper.FileExists(dest_file_1, AsyncFileTestHelper::kDontCheckSize));
+}
+
 }  // namespace storage
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 8b069831..9e28c06 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -8805,1697 +8805,6 @@
       }
     ]
   },
-  "Mac10.15 Tests (dbg)": {
-    "gtest_tests": [
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "absl_hardening_tests",
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "accessibility_unittests",
-        "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/"
-      },
-      {
-        "args": [
-          "angle_unittests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_unittests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "app_shell_unittests",
-        "test_id_prefix": "ninja://extensions/shell:app_shell_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_common_unittests",
-        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_fuzzer_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_heap_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_platform_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_crypto_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_ssl_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cast_unittests",
-        "test_id_prefix": "ninja://media/cast:cast_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cc_unittests",
-        "test_id_prefix": "ninja://cc:cc_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_app_unittests",
-        "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromedriver_unittests",
-        "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "color_unittests",
-        "test_id_prefix": "ninja://ui/color:color_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_unittests",
-        "test_id_prefix": "ninja://components:components_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_nocompile_tests",
-        "test_id_prefix": "ninja://content/test:content_nocompile_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crashpad_tests",
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cronet_tests",
-        "test_id_prefix": "ninja://components/cronet:cronet_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cronet_unittests",
-        "test_id_prefix": "ninja://components/cronet:cronet_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crypto_unittests",
-        "test_id_prefix": "ninja://crypto:crypto_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "device_unittests",
-        "test_id_prefix": "ninja://device:device_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "display_unittests",
-        "test_id_prefix": "ninja://ui/display:display_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "events_unittests",
-        "test_id_prefix": "ninja://ui/events:events_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_browsertests",
-        "test_id_prefix": "ninja://extensions:extensions_browsertests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_unittests",
-        "test_id_prefix": "ninja://extensions:extensions_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "filesystem_service_unittests",
-        "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gcm_unit_tests",
-        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gfx_unittests",
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gin_unittests",
-        "test_id_prefix": "ninja://gin:gin_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "google_apis_unittests",
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gpu_unittests",
-        "test_id_prefix": "ninja://gpu:gpu_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gwp_asan_unittests",
-        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "headless_browsertests",
-        "test_id_prefix": "ninja://headless:headless_browsertests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "headless_unittests",
-        "test_id_prefix": "ninja://headless:headless_unittests/"
-      },
-      {
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ipc_tests",
-        "test_id_prefix": "ninja://ipc:ipc_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "jingle_unittests",
-        "test_id_prefix": "ninja://jingle:jingle_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "latency_unittests",
-        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "libjingle_xmpp_unittests",
-        "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "liburlpattern_unittests",
-        "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_unittests",
-        "test_id_prefix": "ninja://media:media_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "message_center_unittests",
-        "test_id_prefix": "ninja://ui/message_center:message_center_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "midi_unittests",
-        "test_id_prefix": "ninja://media/midi:midi_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_unittests",
-        "test_id_prefix": "ninja://mojo:mojo_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "nacl_loader_unittests",
-        "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "native_theme_unittests",
-        "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "openscreen_unittests",
-        "test_id_prefix": "ninja://chrome/browser/media/router:openscreen_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "pdf_unittests",
-        "test_id_prefix": "ninja://pdf:pdf_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "perfetto_unittests",
-        "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "power_sampler_unittests",
-        "test_id_prefix": "ninja://tools/mac/power:power_sampler_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ppapi_unittests",
-        "test_id_prefix": "ninja://ppapi:ppapi_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "printing_unittests",
-        "test_id_prefix": "ninja://printing:printing_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "remoting_unittests",
-        "test_id_prefix": "ninja://remoting:remoting_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sandbox_mac_unittests",
-        "test_id_prefix": "ninja://sandbox/mac:sandbox_mac_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "service_manager_unittests",
-        "test_id_prefix": "ninja://services/service_manager/tests:service_manager_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "services_unittests",
-        "test_id_prefix": "ninja://services:services_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "shell_dialogs_unittests",
-        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "skia_unittests",
-        "test_id_prefix": "ninja://skia:skia_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "snapshot_unittests",
-        "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sql_unittests",
-        "test_id_prefix": "ninja://sql:sql_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "storage_unittests",
-        "test_id_prefix": "ninja://storage:storage_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_base_unittests",
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_touch_selection_unittests",
-        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "unit_tests",
-        "test_id_prefix": "ninja://chrome/test:unit_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "url_unittests",
-        "test_id_prefix": "ninja://url:url_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "views_unittests",
-        "test_id_prefix": "ninja://ui/views:views_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wtf_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "xr_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "xr_browser_tests",
-        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "zlib_unittests",
-        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "blink_python_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "blink_python_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://:blink_python_tests/"
-      },
-      {
-        "args": [
-          "--num-retries=3",
-          "--debug"
-        ],
-        "experiment_percentage": 100,
-        "isolate_name": "blink_web_tests",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "blink_web_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 40
-        },
-        "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "isolate_name": "content_shell_crash_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "content_shell_crash_test",
-        "resultdb": {
-          "enable": true,
-          "result_format": "single"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/shell:content_shell_crash_test/"
-      },
-      {
-        "isolate_name": "flatbuffers_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "flatbuffers_unittests",
-        "resultdb": {
-          "enable": true,
-          "result_format": "single"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/flatbuffers:flatbuffers_unittests/"
-      },
-      {
-        "isolate_name": "grit_python_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "grit_python_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://tools/grit:grit_python_unittests/"
-      },
-      {
-        "args": [
-          "--pageset-repeat=1",
-          "--test-shard-map-filename=smoke_test_benchmark_shard_map.json",
-          "--browser=debug"
-        ],
-        "isolate_name": "performance_test_suite",
-        "merge": {
-          "args": [
-            "--smoke-test-mode"
-          ],
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "performance_test_suite",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "hard_timeout": 960,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test_id_prefix": "ninja://chrome/test:performance_test_suite/"
-      },
-      {
-        "isolate_name": "telemetry_gpu_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_gpu_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
-      },
-      {
-        "args": [
-          "--jobs=1",
-          "--extra-browser-args=--disable-gpu"
-        ],
-        "isolate_name": "telemetry_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_unittests/"
-      },
-      {
-        "args": [
-          "--gtest-benchmark-name=views_perftests"
-        ],
-        "isolate_name": "views_perftests",
-        "merge": {
-          "args": [
-            "--smoke-test-mode"
-          ],
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "views_perftests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-10.15"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/views:views_perftests/"
-      }
-    ]
-  },
   "Mac11 Tests": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index f03b56f..58a1bdf 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -265,15 +265,6 @@
           'shards': 25,
         },
       },
-      'Mac10.15 Tests (dbg)': {
-        'experiment_percentage': 100,
-        'args': [
-          '--debug',
-        ],
-        'swarming': {
-          'shards': 40,
-        },
-      },
       'Mac11 Tests (dbg)': {
         'experiment_percentage': 100,
         'args': [
@@ -530,7 +521,6 @@
       # off CQ.
       'Mac10.13 Tests', # https://crbug.com/1042757
       'Mac10.15 Tests', # https://crbug.com/1042757
-      'Mac10.15 Tests (dbg)', # https://crbug.com/1201386
       'Mac11 Tests (dbg)', # https://crbug.com/1201386
       'mac-code-coverage', # https://crbug.com/1201386
       'Linux TSan Tests',  # https://crbug.com/368525
@@ -628,21 +618,6 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter',
         ],
       },
-      'Mac10.15 Tests (dbg)': {
-        # crbug.com/1042757
-        'swarming': {
-          'dimension_sets': [
-            {
-              # These shards are liable to time out when running on macmini7,1.
-              'cores': '8|12',
-            }
-          ],
-        },
-        # crbug.com/1196416
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/mac.mac-rel.browser_tests.filter',
-        ],
-      },
       'Mac11 Tests': {
         'ci_only': True,
       },
@@ -1839,9 +1814,6 @@
       'Mac ASan 64 Tests (1)': { # https://crbug.com/1251656
         'experiment_percentage': 100,
       },
-      'Mac10.15 Tests (dbg)': { # https://crbug.com/1265051
-        'experiment_percentage': 100,
-      },
       'Mac11 Tests (dbg)': { # https://crbug.com/1265051
         'experiment_percentage': 100,
       },
@@ -2407,11 +2379,6 @@
   },
   'performance_test_suite': {
     'modifications': {
-      'Mac10.15 Tests (dbg)': {
-        'args': [
-          '--browser=debug',
-        ],
-      },
       'Mac11 Tests (dbg)': {
         'args': [
           '--browser=debug',
@@ -2923,7 +2890,6 @@
       'Mac10.15 Tests',
       # TODO crbug.com/1277277
       'Mac11 Tests',
-      'Mac10.15 Tests (dbg)',
       'Mac11 Tests (dbg)',
       'Linux - Future (dbg)',  # client.v8.chromium
       'Win10 Tests x64',
@@ -2995,11 +2961,6 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.unit_tests_wayland.filter',
         ],
       },
-      'Mac10.15 Tests (dbg)': {
-        'swarming': {
-          'shards': 2,
-        },
-      },
       'Mac11 Tests (dbg)': {
         'swarming': {
           'shards': 2,
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 95e5825..e6c0595 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5039,15 +5039,6 @@
           'isolated_scripts': 'chromium_mac_rel_isolated_scripts',
         },
       },
-      'Mac10.15 Tests (dbg)': {
-        'mixins': [
-            'mac_10.15',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromium_mac_gtests',
-          'isolated_scripts': 'chromium_dbg_isolated_scripts',
-        },
-      },
       'Mac11 Tests': {
         'mixins': [
             'mac_11_x64',
diff --git a/testing/test.gni b/testing/test.gni
index aceed03..a191185 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -23,6 +23,7 @@
 
 if (is_android) {
   import("//build/config/android/config.gni")
+  import("//build/config/android/create_unwind_table.gni")
   import("//build/config/android/extract_unwind_tables.gni")
   import("//build/config/android/rules.gni")
   import("//build/config/sanitizers/sanitizers.gni")
@@ -198,9 +199,16 @@
       if (defined(invoker.add_unwind_tables_in_apk) &&
           invoker.add_unwind_tables_in_apk) {
         _unwind_table_asset_name = "${target_name}_unwind_assets"
-        unwind_table_asset(_unwind_table_asset_name) {
-          library_target = _library_target
-          deps = [ ":$_library_target" ]
+        if (use_android_unwinder_v2) {
+          unwind_table_asset_v2(_unwind_table_asset_name) {
+            library_target = _library_target
+            deps = [ ":$_library_target" ]
+          }
+        } else {
+          unwind_table_asset(_unwind_table_asset_name) {
+            library_target = _library_target
+            deps = [ ":$_library_target" ]
+          }
         }
       }
 
diff --git a/testing/unexpected_passes_common/queries.py b/testing/unexpected_passes_common/queries.py
index b41a64a..38a0e66 100644
--- a/testing/unexpected_passes_common/queries.py
+++ b/testing/unexpected_passes_common/queries.py
@@ -246,9 +246,15 @@
 
     expectation_files = []
     for qr in results_for_each_step.values():
-      expectation_files.extend(
-          self._GetRelevantExpectationFilesForQueryResult(qr))
-    expectation_files = list(set(expectation_files))
+      # None is a special value indicating "use all expectation files", so
+      # handle that.
+      ef = self._GetRelevantExpectationFilesForQueryResult(qr)
+      if ef is None:
+        expectation_files = None
+        break
+      expectation_files.extend(ef)
+    if expectation_files is not None:
+      expectation_files = list(set(expectation_files))
 
     for r in query_results:
       if self._ShouldSkipOverResult(r):
diff --git a/testing/unexpected_passes_common/queries_unittest.py b/testing/unexpected_passes_common/queries_unittest.py
index 39f5dcc..48a7be3f 100755
--- a/testing/unexpected_passes_common/queries_unittest.py
+++ b/testing/unexpected_passes_common/queries_unittest.py
@@ -132,6 +132,62 @@
                           '1234'))
     self.assertEqual(expectation_files, ['foo_expectations'])
 
+  def testValidResultsNoneExpectations(self):
+    """Tests when an implementation uses None for expectation files."""
+    query_results = [
+        {
+            'id':
+            'build-1234',
+            'test_id': ('ninja://chrome/test:telemetry_gpu_integration_test/'
+                        'gpu_tests.pixel_integration_test.'
+                        'PixelIntegrationTest.test_name'),
+            'status':
+            'FAIL',
+            'typ_expectations': [
+                'RetryOnFailure',
+            ],
+            'typ_tags': [
+                'win',
+                'intel',
+            ],
+            'step_name':
+            'step_name',
+        },
+        {
+            'id':
+            'build-1234',
+            'test_id': ('ninja://chrome/test:telemetry_gpu_integration_test/'
+                        'gpu_tests.pixel_integration_test.'
+                        'PixelIntegrationTest.test_name'),
+            'status':
+            'FAIL',
+            'typ_expectations': [
+                'RetryOnFailure',
+            ],
+            'typ_tags': [
+                'win',
+                'nvidia',
+            ],
+            'step_name':
+            'step_name',
+        },
+    ]
+    self._popen_mock.return_value = unittest_utils.FakeProcess(
+        stdout=json.dumps(query_results))
+    with mock.patch.object(
+        self._querier, '_GetRelevantExpectationFilesForQueryResult') as ef_mock:
+      ef_mock.return_value = None
+      results, expectation_files = self._querier.QueryBuilder('builder', 'ci')
+      self.assertEqual(len(results), 2)
+      self.assertIn(
+          data_types.Result('test_name', ['win', 'intel'], 'Failure',
+                            'step_name', '1234'), results)
+      self.assertIn(
+          data_types.Result('test_name', ['win', 'nvidia'], 'Failure',
+                            'step_name', '1234'), results)
+      self.assertIsNone(expectation_files)
+      ef_mock.assert_called_once()
+
   def testValidResultsMultipleSteps(self):
     """Tests functionality when results from multiple steps are present."""
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index b333b16c..b5a94c93 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5026,6 +5026,21 @@
             ]
         }
     ],
+    "MixedContentDownloadDialog": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EnableMixedContentDownloadDialog"
+                    ]
+                }
+            ]
+        }
+    ],
     "MixedFormInterstitial": [
         {
             "platforms": [
@@ -7212,9 +7227,26 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "EnabledFixed",
+                    "enable_features": [
+                        "ShareUsageRanking",
+                        "ShareUsageRankingFixedMore"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "ShareUsageRanking",
+                        "ShareUsageRankingFixedMore"
+                    ]
+                },
+                {
+                    "name": "EnabledVariable",
                     "enable_features": [
                         "ShareUsageRanking"
+                    ],
+                    "disable_features": [
+                        "ShareUsageRankingFixedMore"
                     ]
                 }
             ]
@@ -7668,24 +7700,6 @@
             ]
         }
     ],
-    "TabSwitchMetrics2": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "TabSwitchMetrics2"
-                    ]
-                }
-            ]
-        }
-    ],
     "TabToGTSAnimation": [
         {
             "platforms": [
@@ -9141,5 +9155,35 @@
                 }
             ]
         }
+    ],
+    "WinDelaySpellcheckServiceInit": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "WinDelaySpellcheckServiceInit"
+                    ]
+                }
+            ]
+        }
+    ],
+    "XsurfaceMetricsReporting": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "XsurfaceMetricsReporting"
+                    ]
+                }
+            ]
+        }
     ]
 }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index fc85be92..1de818cd 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -190,7 +190,7 @@
 // allows the element to be enabled by the runtime enabled feature, for origin
 // trials.
 const base::Feature kFencedFrames{"FencedFrames",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
 const base::FeatureParam<FencedFramesImplementationType>::Option
     fenced_frame_implementation_types[] = {
         {FencedFramesImplementationType::kShadowDOM, "shadow_dom"},
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
index bfefbb2..8fb118d 100644
--- a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
+++ b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
@@ -765,6 +765,18 @@
                 ephemeral_line_range.StartPosition()))
           continue;
 
+        // Do not add the grammar marker if it overlaps with existing spellcheck
+        // markers.
+        if (suggestion_type == SuggestionMarker::SuggestionType::kGrammar &&
+            !GetDocument()
+                 .Markers()
+                 .MarkersIntersectingRange(
+                     ToEphemeralRangeInFlatTree(ephemeral_line_range),
+                     DocumentMarker::MarkerTypes::Spelling())
+                 .IsEmpty()) {
+          continue;
+        }
+
         GetDocument().Markers().AddSuggestionMarker(
             ephemeral_line_range,
             SuggestionMarkerProperties::Builder()
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
index a86b50fc..9a81b97 100644
--- a/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
+++ b/third_party/blink/renderer/core/editing/ime/input_method_controller_test.cc
@@ -207,6 +207,37 @@
                 ->GetSuggestionType());
 }
 
+TEST_F(InputMethodControllerTest, AddGrammarCheckSpans) {
+  InsertHTMLElement("<div id='sample' contenteditable>hello world</div>",
+                    "sample");
+  Element* div = GetDocument().QuerySelector("div");
+  Node* text = div->firstChild();
+
+  GetDocument().Markers().AddSpellingMarker(
+      EphemeralRange(Position(text, 0), Position(text, 5)));
+
+  Vector<ImeTextSpan> grammar_ime_text_spans;
+  grammar_ime_text_spans.push_back(ImeTextSpan(
+      ImeTextSpan::Type::kGrammarSuggestion, 3, 6, Color(255, 0, 0),
+      ImeTextSpanThickness::kThin, ImeTextSpanUnderlineStyle::kSolid, 0, 0));
+  grammar_ime_text_spans.push_back(ImeTextSpan(
+      ImeTextSpan::Type::kGrammarSuggestion, 8, 10, Color(255, 0, 0),
+      ImeTextSpanThickness::kThin, ImeTextSpanUnderlineStyle::kSolid, 0, 0));
+
+  Controller().AddImeTextSpansToExistingText(grammar_ime_text_spans, 0, 10);
+  // The first grammar check span should not be added because it overlaps with
+  // the existing spellcheck span.
+  EXPECT_EQ(2u, GetDocument().Markers().Markers().size());
+  EXPECT_EQ(0u, GetDocument().Markers().Markers()[0]->StartOffset());
+  EXPECT_EQ(5u, GetDocument().Markers().Markers()[0]->EndOffset());
+  EXPECT_EQ(DocumentMarker::MarkerType::kSpelling,
+            GetDocument().Markers().Markers()[0]->GetType());
+  EXPECT_EQ(8u, GetDocument().Markers().Markers()[1]->StartOffset());
+  EXPECT_EQ(10u, GetDocument().Markers().Markers()[1]->EndOffset());
+  EXPECT_EQ(DocumentMarker::MarkerType::kSuggestion,
+            GetDocument().Markers().Markers()[1]->GetType());
+}
+
 TEST_F(InputMethodControllerTest, GetImeTextSpans) {
   InsertHTMLElement("<div id='sample' contenteditable>hello world</div>",
                     "sample");
diff --git a/third_party/blink/renderer/core/exported/web_node.cc b/third_party/blink/renderer/core/exported/web_node.cc
index 18bfde4..91a094f 100644
--- a/third_party/blink/renderer/core/exported/web_node.cc
+++ b/third_party/blink/renderer/core/exported/web_node.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/to_v8.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 namespace blink {
 
@@ -230,7 +231,9 @@
   return private_->GetWebPluginContainer();
 }
 
-WebNode::WebNode(Node* node) : private_(node) {}
+WebNode::WebNode(Node* node) : private_(node) {
+  DCHECK(IsMainThread());
+}
 
 WebNode& WebNode::operator=(Node* node) {
   private_ = node;
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index 3997a24..2369ec1 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -212,8 +212,12 @@
     const AttributeModificationParams& params) {
   const QualifiedName& name = params.name;
   if (name == html_names::kValueAttr) {
-    if (HTMLDataListElement* data_list = OwnerDataListElement())
+    if (HTMLDataListElement* data_list = OwnerDataListElement()) {
       data_list->OptionElementChildrenChanged();
+    } else if (HTMLSelectMenuElement* select_menu =
+                   HTMLSelectMenuElement::OwnerSelectMenu(this)) {
+      select_menu->OptionElementValueChanged(*this);
+    }
   } else if (name == html_names::kDisabledAttr) {
     if (params.old_value.IsNull() != params.new_value.IsNull()) {
       PseudoStateChanged(CSSSelector::kPseudoDisabled);
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index cd7de3c7..a23df6b9 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -168,7 +168,7 @@
 
   int first_selection_index = selectedIndex();
 
-  // If a non-placeholer label option is selected (firstSelectionIndex > 0),
+  // If a non-placeholder label option is selected (firstSelectionIndex > 0),
   // it's not value-missing.
   return first_selection_index < 0 ||
          (!first_selection_index && HasPlaceholderLabelOption());
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index fc6e836..78769a5 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -659,6 +659,7 @@
   if (!selected_option_ || new_option_part->Selected()) {
     SetSelectedOption(new_option_part);
   }
+  SetNeedsValidityCheck();
 }
 
 void HTMLSelectMenuElement::OptionPartRemoved(HTMLOptionElement* option_part) {
@@ -681,6 +682,7 @@
     auto* first_option_part = FirstOptionPart();
     SetSelectedOption(first_option_part);
   }
+  SetNeedsValidityCheck();
 }
 
 HTMLOptionElement* HTMLSelectMenuElement::FirstOptionPart() const {
@@ -733,13 +735,25 @@
     selected_option_->SetSelectedState(true);
 
   UpdateSelectedValuePartContents();
+  SetNeedsValidityCheck();
   NotifyFormStateChanged();
 }
 
 void HTMLSelectMenuElement::OptionElementChildrenChanged(
     const HTMLOptionElement& option) {
-  if (selected_option_ == &option)
+  if (selected_option_ == &option) {
+    SetNeedsValidityCheck();
+    NotifyFormStateChanged();
     UpdateSelectedValuePartContents();
+  }
+}
+
+void HTMLSelectMenuElement::OptionElementValueChanged(
+    const HTMLOptionElement& option) {
+  if (selected_option_ == &option) {
+    SetNeedsValidityCheck();
+    NotifyFormStateChanged();
+  }
 }
 
 void HTMLSelectMenuElement::SelectNextOption() {
@@ -894,10 +908,10 @@
     return false;
 
   if (auto* selected_option = SelectedOption()) {
-    // If a non-placeholer label option is selected, it's not value-missing.
-    // TODO(crbug.com/1121840) Sync APIs shouldn't rely on async computed
-    // option_parts_
-    return option_parts_.size() == 1 && selected_option->value().IsEmpty();
+    // If a non-placeholder label option is selected, it's not value-missing.
+    // https://html.spec.whatwg.org/multipage/form-elements.html#placeholder-label-option
+    return selected_option == FirstOptionPart() &&
+           selected_option->value().IsEmpty();
   }
 
   return true;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
index c7763c6..90a805cd 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.h
@@ -47,6 +47,7 @@
   // For use in the implementation of HTMLOptionElement.
   void OptionSelectionStateChanged(HTMLOptionElement*, bool option_is_selected);
   void OptionElementChildrenChanged(const HTMLOptionElement& option);
+  void OptionElementValueChanged(const HTMLOptionElement& option);
 
   PartType AssignedPartType(Node* node) const;
 
diff --git a/third_party/blink/renderer/core/html/link_web_bundle.cc b/third_party/blink/renderer/core/html/link_web_bundle.cc
index 7cf11ad..a9947910 100644
--- a/third_party/blink/renderer/core/html/link_web_bundle.cc
+++ b/third_party/blink/renderer/core/html/link_web_bundle.cc
@@ -42,6 +42,9 @@
 LinkWebBundle::LinkWebBundle(HTMLLinkElement* owner) : LinkResource(owner) {
   UseCounter::Count(owner_->GetDocument().GetExecutionContext(),
                     WebFeature::kSubresourceWebBundles);
+  AddConsoleMessage(
+      "<link rel=\"webbundle\"> is deprecated. See migration guide at "
+      "https://bit.ly/3rpDuEX.");
 }
 LinkWebBundle::~LinkWebBundle() = default;
 
@@ -57,6 +60,10 @@
 }
 
 void LinkWebBundle::OnWebBundleError(const String& message) const {
+  AddConsoleMessage(message);
+}
+
+void LinkWebBundle::AddConsoleMessage(const String& message) const {
   if (!owner_)
     return;
   ExecutionContext* context = owner_->GetDocument().GetExecutionContext();
diff --git a/third_party/blink/renderer/core/html/link_web_bundle.h b/third_party/blink/renderer/core/html/link_web_bundle.h
index f24e8ea..afa6b44 100644
--- a/third_party/blink/renderer/core/html/link_web_bundle.h
+++ b/third_party/blink/renderer/core/html/link_web_bundle.h
@@ -75,6 +75,7 @@
                                CompleteURLCallback complete_url_callback);
 
  private:
+  void AddConsoleMessage(const String& message) const;
   bool ResourcesOrScopesMatch(const KURL& url) const;
   void ReleaseBundleLoader();
 
diff --git a/third_party/blink/renderer/core/html/link_web_bundle_test.cc b/third_party/blink/renderer/core/html/link_web_bundle_test.cc
index 290e6b8d..5dc9a111 100644
--- a/third_party/blink/renderer/core/html/link_web_bundle_test.cc
+++ b/third_party/blink/renderer/core/html/link_web_bundle_test.cc
@@ -92,4 +92,16 @@
       link->ValidResourceUrls().Contains(KURL("https://test2.example.com")));
 }
 
+TEST_F(LinkWebBundleTest, DeprecationMessage) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete("<!DOCTYPE html><link rel=\"webbundle\">");
+
+  EXPECT_TRUE(std::any_of(ConsoleMessages().begin(), ConsoleMessages().end(),
+                          [](const auto& console_message) {
+                            return console_message.Contains(
+                                "<link rel=\"webbundle\"> is deprecated.");
+                          }));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
index b8ece55..7c91451e 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.cc
@@ -37,8 +37,8 @@
 NGFlexItemIterator::Entry NGFlexItemIterator::NextItem() {
   const NGBlockBreakToken* current_child_break_token = nullptr;
   NGFlexItem* current_item = next_unstarted_item_;
-  wtf_size_t current_item_idx = flex_item_idx_ - 1;
-  wtf_size_t current_line_idx = flex_line_idx_;
+  wtf_size_t current_item_idx = 0;
+  wtf_size_t current_line_idx = 0;
 
   if (break_token_) {
     // If we're resuming layout after a fragmentainer break, we'll first resume
@@ -52,6 +52,9 @@
           To<NGBlockBreakToken>(child_break_tokens[child_token_idx_++].Get());
       current_item = FindNextItem(current_child_break_token);
 
+      current_item_idx = flex_item_idx_ - 1;
+      current_line_idx = flex_line_idx_;
+
       if (child_token_idx_ == child_break_tokens.size()) {
         // We reached the last child break token. Prepare for the next unstarted
         // sibling, and forget the parent break token.
@@ -60,8 +63,11 @@
         break_token_ = nullptr;
       }
     }
-  } else if (next_unstarted_item_) {
-    next_unstarted_item_ = FindNextItem();
+  } else {
+    current_item_idx = flex_item_idx_ - 1;
+    current_line_idx = flex_line_idx_;
+    if (next_unstarted_item_)
+      next_unstarted_item_ = FindNextItem();
   }
 
   return Entry(current_item, current_item_idx, current_line_idx,
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index cb71150..e67752d 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -52,10 +52,10 @@
                  child_percentage_size_,
                  &Node().GetDocument()),
       layout_info_for_devtools_(layout_info_for_devtools) {
-  // TODO(almaher): Support multi-line fragmentation.
+  // TODO(almaher): Support multi-line column fragmentation.
   involved_in_block_fragmentation_ =
       InvolvedInBlockFragmentation(container_builder_) &&
-      !algorithm_.IsMultiline();
+      (!algorithm_.IsMultiline() || is_horizontal_flow_);
 }
 
 bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis(
@@ -1169,6 +1169,7 @@
   for (auto entry = item_iterator.NextItem();
        NGFlexItem* flex_item = entry.flex_item;
        entry = item_iterator.NextItem()) {
+    wtf_size_t flex_item_idx = entry.flex_item_idx;
     wtf_size_t flex_line_idx = entry.flex_line_idx;
     NGFlexLine& line_output = (*flex_line_outputs)[flex_line_idx];
     const NGBreakToken* item_break_token = entry.token;
@@ -1193,7 +1194,8 @@
     // TODO(almaher): Once we add support for row break tokens, set
     // |has_inflow_child_break_inside_| to false when adding item break tokens.
     if (container_builder_.HasInflowChildBreakInside() &&
-        !is_horizontal_flow_) {
+        (!is_horizontal_flow_ ||
+         last_line_idx_to_process_first_child_ != flex_line_idx)) {
       // But if the break happened in the same flow, we'll now just finish
       // layout of the fragment. No more siblings should be processed.
       break;
@@ -1231,8 +1233,13 @@
     // containers.
     NGBreakStatus break_status = NGBreakStatus::kContinue;
     if (!early_break_ && ConstraintSpace().HasBlockFragmentation()) {
-      bool has_container_separation =
-          last_line_idx_to_process_first_child_ == flex_line_idx;
+      bool has_container_separation = false;
+      if (is_horizontal_flow_) {
+        has_container_separation = has_processed_first_line_;
+      } else {
+        has_container_separation =
+            last_line_idx_to_process_first_child_ == flex_line_idx;
+      }
       break_status = BreakBeforeChildIfNeeded(
           ConstraintSpace(), flex_item->ng_input_node, *layout_result,
           ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset,
@@ -1241,7 +1248,14 @@
 
     if (break_status == NGBreakStatus::kBrokeBefore) {
       ConsumeRemainingFragmentainerSpace(line_output);
-      return NGLayoutResult::kSuccess;
+      // If we broke before an item in a row container, make sure that all
+      // items in that row have been processed before returning.
+      if (!is_horizontal_flow_ ||
+          flex_item_idx == line_output.line_items.size() - 1) {
+        return NGLayoutResult::kSuccess;
+      }
+      last_line_idx_to_process_first_child_ = flex_line_idx;
+      continue;
     }
     if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
       return NGLayoutResult::kNeedsEarlierBreak;
@@ -1280,6 +1294,10 @@
       PropagateBaselineFromChild(flex_item->Style(), fragment,
                                  offset.block_offset, &fallback_baseline);
     }
+    if (!has_processed_first_line_ &&
+        flex_item_idx == line_output.line_items.size() - 1) {
+      has_processed_first_line_ = true;
+    }
     last_line_idx_to_process_first_child_ = flex_line_idx;
   }
 
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index 3edccfd..f160420 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -124,8 +124,12 @@
 
   // This will be set during block fragmentation once we've processed the first
   // flex item in a given line. It is used to check if we're at a valid class A
-  // or B breakpoint within a column flex container.
+  // or C breakpoint within a column flex container.
   wtf_size_t last_line_idx_to_process_first_child_ = kNotFound;
+  // This will be set during block fragmentation once we've processed the first
+  // flex line. It is used to check if we're at a valid class A or C breakpoint
+  // within a row flex container.
+  bool has_processed_first_line_ = false;
 
   FlexLayoutAlgorithm algorithm_;
   DevtoolsFlexInfo* layout_info_for_devtools_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index e1e43799..f0b2ff0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -290,6 +290,24 @@
                                        /* relative_offset */ LogicalOffset()));
 }
 
+void NGBoxFragmentBuilder::RemoveOldLegacyOOFFlexItem(
+    const LayoutObject& object) {
+  // While what this method does should "work" for any child fragment in
+  // general, it's only expected to be called under very specific legacy
+  // circumstances, and besides it's evil.
+  DCHECK(object.IsOutOfFlowPositioned());
+  DCHECK(object.Parent()->IsFlexibleBox());
+  DCHECK(object.Parent()->IsOutOfFlowPositioned());
+  for (wtf_size_t idx = 0; idx < children_.size(); idx++) {
+    const ChildWithOffset& child = children_[idx];
+    if (child.fragment->GetLayoutObject() == &object) {
+      children_.EraseAt(idx);
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
 NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const {
   if (box_type_ != NGPhysicalFragment::NGBoxType::kNormalBox)
     return box_type_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index b5d35a62..698331ef 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -223,6 +223,14 @@
                                    const NGLogicalStaticPosition&,
                                    const LayoutInline* inline_container);
 
+  // Remove the fragment previously generated for an out-of-flow positioned flex
+  // item inside an out-of-flow legacy flex container. This is a work-around for
+  // OOFs being laid out out-of-document-order, which is an issue with the
+  // legacy engine (although it's not known to cause any other actual problems
+  // than this). We'll call this method to correct a document-out-of-order
+  // issue.
+  void RemoveOldLegacyOOFFlexItem(const LayoutObject&);
+
   // Before layout we'll determine whether we can tell for sure that the node
   // (or what's left of it to lay out, in case we've already broken) will fit in
   // the current fragmentainer. If this is the case, we'll know that any
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index c50eb34f..f055584 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -197,6 +197,7 @@
     return;
 
   wtf_size_t prev_placed_objects_size = placed_objects.size();
+  bool did_get_same_object_count_once = false;
   while (SweepLegacyCandidates(&placed_objects)) {
     container_builder_->SwapOutOfFlowPositionedCandidates(&candidates);
 
@@ -214,8 +215,18 @@
     // any additional objects.
     wtf_size_t placed_objects_size = placed_objects.size();
     if (prev_placed_objects_size == placed_objects_size) {
-      NOTREACHED();
-      break;
+      if (did_get_same_object_count_once || !has_legacy_flex_box_) {
+        NOTREACHED();
+        break;
+      }
+      // If we have an OOF legacy flex container with an (uncontained;
+      // e.g. fixed inside absolute positioned) OOF flex item inside, we'll
+      // allow one additional iteration, even if the object count is the
+      // same. In the first iteration the objects in
+      // LayoutBlock::PositionedObjects() were not in document order, and then
+      // corrected afterwards (before we get here). Only allow this to happen
+      // once, to avoid infinite loops for whatever reason, and good fortune.
+      did_get_same_object_count_once = true;
     }
     prev_placed_objects_size = placed_objects_size;
   }
@@ -238,11 +249,28 @@
     return false;
   TrackedLayoutBoxLinkedHashSet* legacy_objects =
       container_block->PositionedObjects();
-  if (!legacy_objects || legacy_objects->size() == placed_objects->size())
-    return false;
+  if (!legacy_objects || legacy_objects->size() == placed_objects->size()) {
+    if (!has_legacy_flex_box_ || performing_extra_legacy_check_)
+      return false;
+    // If there is an OOF legacy flex container, and PositionedObjects() are out
+    // of document order (which is something that can happen in the legacy
+    // engine when there's a fixed-positioned object inside an absolute-
+    // positioned object - and we should just live with that and eventually get
+    // rid of the legacy engine), we'll allow one more pass, in case there's a
+    // fixed-positioend OOF flex item inside an absolutely-positioned OOF flex
+    // container. Because at this point, PositionedObjects() should finally be
+    // in correct document order. Only allow one more additional pass, though,
+    // since we might get stuck in an infinite loop otherwise (for reasons
+    // currently unknown).
+    performing_extra_legacy_check_ = true;
+  }
+  bool candidate_added = false;
   for (LayoutObject* legacy_object : *legacy_objects) {
-    if (placed_objects->Contains(legacy_object))
-      continue;
+    if (placed_objects->Contains(legacy_object)) {
+      if (!performing_extra_legacy_check_ || !legacy_object->NeedsLayout())
+        continue;
+      container_builder_->RemoveOldLegacyOOFFlexItem(*legacy_object);
+    }
 
     // Flex OOF children may have center alignment or similar, and in order
     // to determine their static position correctly need to have a valid
@@ -265,6 +293,13 @@
       }
     }
 
+    // If we have a legacy OOF flex container, we'll allow some rocket science
+    // to take place, as an attempt to get things laid out in correct document
+    // order, or we might otherwise leave behind objects (OOF flex items)
+    // needing layout.
+    if (!has_legacy_flex_box_)
+      has_legacy_flex_box_ = layout_box->IsFlexibleBox();
+
     NGLogicalStaticPosition static_position =
         LayoutBoxUtils::ComputeStaticPositionFromLegacy(
             *layout_box,
@@ -280,8 +315,9 @@
     container_builder_->AddOutOfFlowLegacyCandidate(
         NGBlockNode(layout_box), static_position,
         DynamicTo<LayoutInline>(css_container));
+    candidate_added = true;
   }
-  return true;
+  return candidate_added;
 }
 
 void NGOutOfFlowLayoutPart::HandleFragmentation() {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 0edcc104..f68587b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -350,6 +350,16 @@
   bool has_block_fragmentation_ = false;
   // A fixedpos containing block was found in an outer fragmentation context.
   bool outer_context_has_fixedpos_container_ = false;
+
+  // Set to true if there's a legacy flexbox inside a (non-containing) legacy
+  // object (so that it's found in LayoutBlock::PositionedObjects()). E.g.:
+  //
+  // <div style="position:relative;">
+  //   <div id="legacy" style="columns:2;">
+  //     <div style="display:flex; position:absolute;">
+  bool has_legacy_flex_box_ = false;
+
+  bool performing_extra_legacy_check_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index f6a2b2b..8a78be2 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -3717,7 +3717,9 @@
   layer->PushPropertiesTo(
       layer->CreateLayerImpl(host_impl.active_tree()).get(),
       *const_cast<const cc::LayerTreeHost&>(GetLayerTreeHost())
-           .pending_commit_state());
+           .pending_commit_state(),
+      const_cast<const cc::LayerTreeHost&>(GetLayerTreeHost())
+          .thread_unsafe_commit_state());
   Update(artifact2);
   ASSERT_EQ(1u, LayerCount());
   ASSERT_EQ(layer, LayerAt(0));
@@ -3742,7 +3744,9 @@
   layer->PushPropertiesTo(
       layer->CreateLayerImpl(host_impl.active_tree()).get(),
       *const_cast<const cc::LayerTreeHost&>(GetLayerTreeHost())
-           .pending_commit_state());
+           .pending_commit_state(),
+      const_cast<const cc::LayerTreeHost&>(GetLayerTreeHost())
+          .thread_unsafe_commit_state());
   Update(artifact3);
   ASSERT_EQ(1u, LayerCount());
   ASSERT_EQ(layer, LayerAt(0));
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e720f49..d049e7d 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -281,7 +281,14 @@
     },
     {
       name: "BarcodeDetector",
-      status: "stable",
+      status: {
+        // Built-in barcode detection APIs are only available from some
+        // platforms. See //services/shape_detection.
+        "Android": "stable",
+        "ChromeOS": "stable",
+        "Mac": "stable",
+        "default": "test",
+      },
     },
     {
       // https://github.com/chrishtr/battery-savings/blob/master/explainer.md
diff --git a/third_party/blink/tools/blinkpy/common/config/builders.json b/third_party/blink/tools/blinkpy/common/config/builders.json
index 6128a221..acd3f82 100644
--- a/third_party/blink/tools/blinkpy/common/config/builders.json
+++ b/third_party/blink/tools/blinkpy/common/config/builders.json
@@ -34,11 +34,6 @@
         "port_name": "mac-mac10.15",
         "specifiers": ["Mac10.15", "Release"]
     },
-    "Mac10.15 Tests (dbg)": {
-        "master": "chromium.mac",
-        "port_name": "mac-mac10.15",
-        "specifiers": ["Mac10.15", "Debug"]
-    },
     "Mac11 Tests (dbg)": {
         "master": "chromium.mac",
         "port_name": "mac-mac11",
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index cf761f7..fe440b9 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1111,7 +1111,6 @@
 external/wpt/battery-status/battery-plugging-in-manual.https.html [ Skip ]
 external/wpt/battery-status/battery-unplugging-manual.https.html [ Skip ]
 external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html [ Skip ]
-external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html [ Skip ]
 external/wpt/clipboard-apis/clipboard-file-manual.html [ Skip ]
 external/wpt/clipboard-apis/events/copy-event-manual.html [ Skip ]
 external/wpt/clipboard-apis/events/cut-event-manual.html [ Skip ]
diff --git a/third_party/blink/web_tests/SmokeTests b/third_party/blink/web_tests/SmokeTests
index f4226f3..46e526c 100644
--- a/third_party/blink/web_tests/SmokeTests
+++ b/third_party/blink/web_tests/SmokeTests
@@ -768,7 +768,7 @@
 html/details_summary/details-clone.html
 html/details_summary/details-element-render-inline-crash.html
 html/details_summary/details-summary-document-child.html
-html/dialog/inert-label-focus.html
+external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
 html/grouping_content/figure-element.html
 html/marquee/marquee-inside-template-tag-crash.html
 html/sections/article-element.html
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index df76131..8bcc5c23 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -249,12 +249,6 @@
 crbug.com/1093466 virtual/portals/http/tests/portals/* [ Pass ]
 crbug.com/1093466 virtual/portals/wpt_internal/portals/* [ Pass ]
 
-# These tests require fenced-frames
-# Keep this in sync with VirtualTestSuites.
-crbug.com/1123606 wpt_internal/fenced_frame/* [ Skip ]
-crbug.com/1123606 fenced_frame/* [ Skip ]
-crbug.com/1123606 virtual/fenced-frame-shadow-dom/* [ Pass ]
-
 # These tests require the experimental prerender feature.
 # See https://crbug.com/1126305.
 crbug.com/1126305 external/wpt/speculation-rules/prerender/* [ Skip ]
@@ -2508,6 +2502,7 @@
 crbug.com/893480 external/wpt/html/semantics/forms/the-input-element/checkable-active-onblur.html [ Failure Timeout ]
 crbug.com/893480 external/wpt/html/semantics/forms/the-button-element/active-onblur.html [ Failure Timeout ]
 crbug.com/893480 external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-canceling.html [ Failure ]
+crbug.com/893480 external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html [ Failure ]
 
 # needs implementation of test_driver_internal.action_sequence
 # for these tests there is an exception when scrolling: element click intercepted error
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 968fb8f..377fa8f 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1038,7 +1038,7 @@
       "http/tests/inspector-protocol/target/auto-attach-auction-worklet.js"
     ],
     "args": [
-      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,FencedFrames:implementation_type/shadow_dom"
+      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge"
     ]
   },
   {
@@ -1078,14 +1078,6 @@
     "args": ["--enable-features=FencedFrames:implementation_type/mparch,Prerender2"]
   },
   {
-    "prefix": "fenced-frame-shadow-dom",
-    "bases": [
-      "fenced_frame",
-      "wpt_internal/fenced_frame"
-    ],
-    "args": ["--enable-features=FencedFrames:implementation_type/shadow_dom,Prerender2"]
-  },
-  {
     "prefix": "disable-custom-element-default-style",
     "bases": ["external/wpt/css/css-cascade/presentational-hints-cascade.html"],
     "args": ["--disable-blink-features=CustomElementDefaultStyle"]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 7798277..23a2d04 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -497,7 +497,6 @@
 crbug.com/1050754 external/wpt/client-hints/sec-ch-ua.https.html [ Failure ]
 crbug.com/1050754 external/wpt/clipboard-apis/async-html-script-removal.https.html [ Crash ]
 crbug.com/1050754 external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html [ Failure ]
-crbug.com/1050754 external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html [ Failure ]
 crbug.com/1050754 external/wpt/clipboard-apis/async-svg-script-removal.https.html [ Crash ]
 crbug.com/1050754 external/wpt/clipboard-apis/async-write-blobs-read-blobs.https.html [ Crash Failure ]
 crbug.com/1050754 external/wpt/clipboard-apis/async-write-html-read-html.https.html [ Crash ]
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html
index 373c4a1..9bb2b9d5 100644
--- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-custom-formats-write-read.tentative.https.html
@@ -12,6 +12,8 @@
 'use strict';
 
 promise_test(async t => {
+  await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+  await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
   const format1 = 'application/x-custom-format-clipboard-test-format-1';
   const format2 = 'application/x-custom-format-clipboard-test-format-2';
   const blobInput1 = new Blob(['input data 1'], {type: format1});
@@ -49,8 +51,3 @@
   assert_equals(data2, 'input data 2');
 }, 'Verify write and read clipboard given 2 platform-neutral custom format inputs');
 </script>
-<p>
-  This is a manual test because it writes/reads to the shared system
-  clipboard and thus cannot be run async with other tests that might interact
-  with the clipboard.
-</p>
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html
new file mode 100644
index 0000000..887ba8a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard unsanitized HTML write -> Async Clipboard unsanitized HTML read test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<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="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+// This function removes extra spaces between tags in html. For example, the
+// following html: "<p> Hello </p>   <body> World </body>" would turn into this
+//           html: "<p> Hello </p> <body> World </body>"
+// We remove the extra spaces because in html they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatHtml(html) {
+  const parser = new DOMParser();
+  const htmlString =
+        parser.parseFromString(html, 'text/html').documentElement.innerHTML;
+  const reformattedString = htmlString.replace(/\>\s*\</g, '> <');
+  return reformattedString;
+}
+
+// Writes a payload with custom content and checks to ensure the correct data
+// was written successfully.
+promise_test(async t => {
+  await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+  await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+  // Create and write unsanitized version of standard HTML and custom formats.
+  const format1 = 'text/html';
+  const format2 = 'application/x-custom-format-clipboard-test-format-2';
+  const textInput = '<style>p {color:blue}</style><p>Hello World</p>';
+  const blobInput1 = new Blob([textInput], {type: 'text/html'});
+  const blobInput2 = new Blob(['input data 2'], {type: format2});
+  const clipboardItemInput = new ClipboardItem(
+      {[format1]: blobInput1, [format2]: blobInput2},
+      {unsanitized: [format1, format2]});
+  await waitForUserActivation();
+  await navigator.clipboard.write([clipboardItemInput]);
+
+  // Read unsanitized version of HTML format.
+  await waitForUserActivation();
+  const clipboardItems = await navigator.clipboard.read(
+    {unsanitized: [format1, format2]});
+
+  assert_equals(clipboardItems.length, 1);
+  const clipboardItem = clipboardItems[0];
+  assert_true(clipboardItem instanceof ClipboardItem);
+
+  const blobOutput1 = await clipboardItem.getType(format1);
+  assert_equals(blobOutput1.type, format1);
+  const data1 = await (new Response(blobOutput1)).text();
+  const outputHtml = reformatHtml(data1);
+  const inputHtml = reformatHtml(textInput);
+  assert_equals(outputHtml, inputHtml);
+
+  const blobOutput2 = await clipboardItem.getType(format2);
+  assert_equals(blobOutput2.type, format2);
+  const data2 = await (new Response(blobOutput2)).text();
+  assert_equals(data2, 'input data 2');
+}, 'Verify write and read unsanitized content to the clipboard given text/html format as input');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html
new file mode 100644
index 0000000..f44ed22
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard unsanitized write -> Async Clipboard unsanitized read test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<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="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+// Writes a payload with custom content and checks to ensure the correct data
+// was written successfully.
+promise_test(async t => {
+  await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+  await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+  const dataToWrite = 'Test text.';
+  const format = 'text/plain';
+
+  const blobInput = new Blob([dataToWrite], {type: format});
+  // Blob types are automatically converted to lower-case.
+  assert_equals(blobInput.type, format.toLowerCase());
+  const clipboardItemInput = new ClipboardItem(
+      {[format]: blobInput}, {unsanitized: [format]});
+  await waitForUserActivation();
+  await navigator.clipboard.write([clipboardItemInput]);
+
+  // Items should be readable on a system clipboard after custom format write.
+  await waitForUserActivation();
+  const clipboardItems = await navigator.clipboard.read(
+    {unsanitized: [format]});
+  assert_equals(clipboardItems.length, 1);
+  const clipboardItem = clipboardItems[0];
+  assert_true(clipboardItem instanceof ClipboardItem);
+
+  const blobOutput = await clipboardItem.getType(format);
+  assert_equals(blobOutput.type, format);
+  const data = await (new Response(blobOutput)).text();
+  assert_equals(data, dataToWrite);
+
+  // These examples use native text formats, so these formats should be
+  // accessible as text.
+  const textOutput = await navigator.clipboard.readText();
+  assert_equals(textOutput, dataToWrite);
+}, 'Verify write and read unsanitized content to the clipboard given standard format as input');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_document_cookie.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_document_cookie.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_document_cookie.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_document_cookie.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_and_no_value.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_and_no_value.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_and_no_value.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_and_no_value.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_equals_in_value.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_equals_in_value.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_equals_in_value.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_equals_in_value.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_multiple_values.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_multiple_values.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_multiple_values.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/change_eventhandler_for_no_name_multiple_values.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_empty.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_empty.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_empty.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_empty.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_multiple.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_multiple.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_multiple.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_multiple.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_single.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_single.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_single.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStoreManager_getSubscriptions_single.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_basic.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_basic.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_basic.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_basic.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_arguments.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_arguments.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_arguments.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_arguments.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_basic.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_basic.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_basic.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_basic.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_delete.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_delete.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_delete.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_delete.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_overwrite.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_overwrite.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_overwrite.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_event_overwrite.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_arguments.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_arguments.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_arguments.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_arguments.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_multiple.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_multiple.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_multiple.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_multiple.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_basic.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_basic.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_basic.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_basic.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_arguments.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_arguments.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_arguments.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_arguments.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_delete_basic.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_delete_basic.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_delete_basic.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_delete_basic.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_frames.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_frames.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_frames.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_frames.https.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_origins.tentative.sub.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_origins.sub.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_origins.tentative.sub.https.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_across_origins.sub.https.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_basic.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_basic.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_basic.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_basic.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_ordering.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_ordering.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_ordering.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_ordering.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_in_detached_frame.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_in_detached_frame.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_in_detached_frame.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_in_detached_frame.https.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_opaque_origin.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_opaque_origin.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_opaque_origin.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_opaque_origin.https.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_special_names.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_special_names.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_special_names.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_special_names.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscribe_arguments.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscribe_arguments.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscribe_arguments.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscribe_arguments.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscriptions_empty.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscriptions_empty.https.window.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscriptions_empty.tentative.https.window.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_subscriptions_empty.https.window.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_cross_origin.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_cross_origin.https.sub.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_cross_origin.tentative.https.sub.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_cross_origin.https.sub.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_reset.tentative.https.html b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_reset.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_reset.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookieStore_subscriptions_reset.https.html
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_mismatched_subscription.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_mismatched_subscription.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_mismatched_subscription.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_mismatched_subscription.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_multiple_subscriptions.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_multiple_subscriptions.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_multiple_subscriptions.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_multiple_subscriptions.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_overlapping_subscriptions.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_overlapping_subscriptions.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_overlapping_subscriptions.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_overlapping_subscriptions.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_single_subscription.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_single_subscription.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_single_subscription.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_cookiechange_eventhandler_single_subscription.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_oncookiechange_eventhandler_single_subscription.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_oncookiechange_eventhandler_single_subscription.https.any.js
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_oncookiechange_eventhandler_single_subscription.tentative.https.any.js
rename to third_party/blink/web_tests/external/wpt/cookie-store/serviceworker_oncookiechange_eventhandler_single_subscription.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-018.html b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-018.html
new file mode 100644
index 0000000..39118d61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/normal-flow/block-in-inline-insert-018.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta name="assert" content="Test inserting a block-in-inline in the middle of existing block-in-inline">
+<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#anonymous-block-level" />
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <div>1</div>
+  <span>
+    <div id="b2">2</div>
+    <div id="b4">4</div>
+  </span>
+<script>
+document.body.offsetTop;
+
+// Both "2" and "4" are block-in-inline children.
+// Insert a block child "3" between them.
+const b3 = document.createElement('div');
+b3.appendChild(document.createTextNode('3'));
+b4.parentElement.insertBefore(b3, b4);
+
+// Check if "3" is between "2" and "4".
+test(() => {
+  assert_greater_than(b3.offsetTop, b2.offsetTop);
+  assert_less_than(b3.offsetTop, b4.offsetTop);
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/fixed-flex-item-inside-abs-flex-in-multicol-crash.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/fixed-flex-item-inside-abs-flex-in-multicol-crash.html
new file mode 100644
index 0000000..29ba567
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/fixed-flex-item-inside-abs-flex-in-multicol-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1269228">
+<div id="elm" style="contain:layout; width:200px;">
+  <div style="columns:2;">
+    <div style="display:flex; padding:10%; position:absolute;">
+      <div style="position:fixed;"></div>
+    </div>
+  </div>
+</div>
+<script>
+  document.body.offsetTop;
+  elm.style.width = "199px";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-001.html
new file mode 100644
index 0000000..ba6b010
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-001.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>
+  Simple multi-line row flex fragmentation.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    background: red;
+    column-count: 5;
+    column-fill:auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    height: 500px;
+    width: 20px;
+  }
+  .flex > div {
+    background: green;
+    height: 250px;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-002.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-002.html
new file mode 100644
index 0000000..62c1e313
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-002.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>
+  Multi-line row flex fragmentation with item overflow.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    column-count: 2;
+    column-fill:auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    height: 200px;
+    width: 50px;
+  }
+  .flex > div {
+    height: 100px;
+    width: 50px;
+  }
+  .flex > div > div {
+    background: green;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div>
+      <div style="height: 100px;"></div>
+      <div style="height: 100px; background: red;"></div>
+    </div>
+    <div>
+      <div style="height: 100px;"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-003.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-003.html
new file mode 100644
index 0000000..4d1b7e1c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-003.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>
+  Multi-line row flex fragmentation intrinsic block size.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    background: red;
+    column-count: 4;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    width: 25px;
+    background: green;
+  }
+  .flex > div {
+    width: 25px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div style="height: 50px;"></div>
+    <div style="height: 350px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-004.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-004.html
new file mode 100644
index 0000000..a0046974
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-004.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>
+  Multi-line row flex fragmentation.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    background: red;
+    column-count: 5;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    width: 20px;
+  }
+  .flex > div {
+    background: green;
+    width: 10px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div style="height: 150px;"></div>
+    <div style="height: 150px;"></div>
+    <div style="height: 350px;"></div>
+    <div style="height: 350px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-005.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-005.html
new file mode 100644
index 0000000..d1e4f97
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-005.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>
+  Multi-line row flex fragmentation with nested OOF.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    background: red;
+    column-count: 4;
+    column-fill: auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    position: relative;
+    height: 400px;
+    width: 25px;
+  }
+  .abs {
+    background: green;
+    position: absolute;
+    width: 25px;
+    top: 0;
+    bottom: 0;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div>
+      <div class="abs"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-006.html b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-006.html
new file mode 100644
index 0000000..1b93be6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-006.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<title>
+  Simple multi-line row flex fragmentation with items that stretch.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#pagination">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+  .multicol {
+    background: red;
+    column-count: 4;
+    column-fill:auto;
+    column-gap: 0px;
+    height: 100px;
+    width: 100px;
+  }
+  .flex {
+    display: flex;
+    flex-wrap: wrap;
+    height: 400px;
+    width: 25px;
+  }
+  .flex > div {
+    background: green;
+    width: 25px;
+  }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="multicol">
+  <div class="flex">
+    <div></div>
+    <div></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-validity.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-validity.tentative.html
index c103f9c7..a58fe54 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-validity.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-validity.tentative.html
@@ -32,8 +32,36 @@
 
   let emptyOption = document.createElement('option');
   selectMenu.appendChild(emptyOption);
-  // TODO(crbug.com/1121840) Sync APIs shouldn't rely on async computed option_parts_
-  // assert_true(selectMenu.checkValidity(), "An empty non-placeholder option should be a valid choice.");
+  assert_false(selectMenu.checkValidity(), "A selected placeholder option should invalidate the selectmenu even if there are multiple options.");
+  emptyOption.selected = true;
+  assert_true(selectMenu.checkValidity(), "An empty non-placeholder option should be a valid choice.");
+
+  let filledOption = document.createElement('option');
+  filledOption.value = "test";
+  selectMenu.appendChild(filledOption);
+  filledOption.selected = true;
+  assert_equals(selectMenu.value, "test", "The non-empty value should be set.");
+  assert_true(selectMenu.checkValidity(), "A non-empty non-placeholder option should be a valid choice.");
+
+  selectMenu.removeChild(option);
+  selectMenu.appendChild(emptyOption);
+  emptyOption.selected = true;
+  assert_equals(selectMenu.value, "", "The empty value should be set.");
+  assert_true(selectMenu.checkValidity(), "Only the first option can be seen as a placeholder.");
+
+  selectMenu.removeChild(filledOption);
+  assert_false(selectMenu.checkValidity(), "A selected placeholder option should invalidate the selectmenu.");
+
+  emptyOption.value = "test2";
+  assert_equals(selectMenu.value, "test2");
+  assert_true(selectMenu.checkValidity(), "A non-empty option value should be a valid choice.");
+
+  emptyOption.removeAttribute("value");
+  assert_equals(selectMenu.value, "");
+  assert_false(selectMenu.checkValidity());
+  emptyOption.innerText = "test";
+  assert_equals(selectMenu.value, "test");
+  assert_true(selectMenu.checkValidity(), "A non-empty option should be a valid choice.");
 
   const selectMenu1 = document.getElementById('selectmenu1');
   assert_equals(selectMenu1.value, "one");
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
new file mode 100644
index 0000000..8acdb570
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-inlines.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=241699">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, click on all the "Click me"s.
+The test fails if you see red.
+</p>
+
+<style>
+dialog {
+  width: 50px;
+}
+</style>
+
+<a id="a" href="javascript:void(0)">Click me</a>
+<button id="button">Click me</button>
+<div id="div" style="background-color: blue; width: 50px; height: 50px">Click meeee</div>
+<span id="span">Click me</span>
+<div id="dialog-parent" style="width: 50px; height: 50px">
+  <span id="dialog-sibling">Click meeee</span>
+  <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+  async function clickOn(element) {
+    let absoluteTop = 0;
+    let absoluteLeft = 0;
+    for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+      absoluteLeft += parentNode.offsetLeft;
+      absoluteTop += parentNode.offsetTop;
+    }
+
+    const x = absoluteLeft + element.offsetWidth / 2;
+    const y = absoluteTop + element.offsetHeight / 2;
+    const actions = new test_driver.Actions()
+      .pointerMove(x, y)
+      .pointerDown()
+      .pointerUp()
+      .pointerMove(0, 0);
+    await actions.send();
+  }
+
+  function eventFiredOnInertElement(e) {
+    e.target.style.background = 'red';
+    inertElementFiredOn = true;
+  }
+
+  inertElements = ['a', 'button', 'div', 'span']
+  inertElements.forEach(function(id) {
+    element = document.getElementById(id);
+    element.addEventListener('click', eventFiredOnInertElement);
+    element.addEventListener('mousemove', eventFiredOnInertElement);
+  });
+
+  document.addEventListener('click', function(e) {
+    document.firedOn = true;
+  });
+
+  document.getElementById('dialog-parent').addEventListener('click', function(e) {
+    e.target.firedOn = true;
+  });
+
+  document.querySelector('dialog').showModal();
+  for (const id of inertElements) {
+    expectedTarget = document;
+    if (id == 'dialog-sibling')
+      expectedTarget = document.getElementById('dialog-parent')
+    element = document.getElementById(id);
+    inertElementFiredOn = false;
+    expectedTarget.firedOn = false;
+    await clickOn(element);
+    assert_false(inertElementFiredOn, 'clicking on ' + id);
+    assert_true(expectedTarget.firedOn, 'clicking on ' + id);
+  }
+}, 'Tests that inert inlines do not receive mouse events.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
new file mode 100644
index 0000000..ee0c91c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=242848">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<label for="submit">Label for Submit</label>
+<dialog>
+  <input id="text" type="text">
+  <input id="submit" type="submit">
+</dialog>
+
+<script>
+promise_test(async () => {
+  async function clickOn(element) {
+    let absoluteTop = 0;
+    let absoluteLeft = 0;
+    for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+      absoluteLeft += parentNode.offsetLeft;
+      absoluteTop += parentNode.offsetTop;
+    }
+
+    const x = absoluteLeft + element.offsetWidth / 2;
+    const y = absoluteTop + element.offsetHeight / 2;
+    const actions = new test_driver.Actions()
+      .pointerMove(x, y)
+      .pointerDown()
+      .pointerUp()
+      .pointerMove(0, 0);
+    await actions.send();
+  }
+
+  document.querySelector('dialog').showModal();
+  document.querySelector('#text').focus();
+
+  label = document.querySelector('label');
+  label.focus();
+  assert_equals(document.activeElement, document.querySelector('#submit'),
+    'label.focus() should send focus to the target.');
+
+  await clickOn(label);
+  assert_equals(document.activeElement, document.body,
+    'Clicking the label should be the same as clicking the document body.');
+}, 'Tests focusing of an inert label for a non-inert target.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
new file mode 100644
index 0000000..393e5e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-uneditable.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<span id="not-editable" contenteditable>I'm not editable while the dialog is showing.</span>
+<dialog>
+  <span id="editable" contenteditable>I'm editable.</span>
+</dialog>
+
+<script>
+promise_test(async () => {
+  async function clickOn(element) {
+    let absoluteTop = 0;
+    let absoluteLeft = 0;
+    for (let parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
+      absoluteLeft += parentNode.offsetLeft;
+      absoluteTop += parentNode.offsetTop;
+    }
+
+    const x = absoluteLeft + element.offsetWidth / 2;
+    const y = absoluteTop + element.offsetHeight / 2;
+    const actions = new test_driver.Actions()
+      .pointerMove(x, y)
+      .pointerDown()
+      .pointerUp()
+      .pointerMove(0, 0);
+    await actions.send();
+  }
+
+  dialog = document.querySelector('dialog');
+  dialog.showModal();
+  notEditable = document.querySelector('#not-editable');
+  editable = document.querySelector('#editable');
+
+  await clickOn(notEditable);
+  oldValue = notEditable.textContent;
+  await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+  assert_equals(notEditable.textContent, oldValue);
+
+  await clickOn(editable);
+  oldValue = editable.textContent;
+  await (new test_driver.Actions().keyDown('a').keyUp('a').send());
+  assert_not_equals(editable.textContent, oldValue);
+
+  notEditable.remove();
+  editable.remove();
+}, 'Test that inert nodes cannot be edited. The test passes if the only text you can edit is in the dialog.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
new file mode 100644
index 0000000..2889e1e9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-node-is-unselectable.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=252071">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+Here is a text node you can't select while the dialog is open.
+<dialog>I'm selectable.</dialog>
+
+<script>
+test(() => {
+  const dialog = document.querySelector('dialog');
+  dialog.showModal();
+  document.execCommand('SelectAll');
+  assert_equals(window.getSelection().toString(), "I'm selectable.");
+}, 'Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
new file mode 100644
index 0000000..c6bcb5d4c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=329407">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+#ancestor {
+  position: absolute;
+  height: 50px;
+  width: 50px;
+  top: 200px;
+  left: 100px;
+  border: 1px solid;
+}
+
+dialog {
+  height: 50px;
+  width: 50px;
+  top: 200px;
+  left: 200px;
+  margin: 0;
+}
+
+dialog::backdrop {
+  display: none;
+}
+</style>
+
+<div id="ancestor">
+  <dialog></dialog>
+</div>
+
+<script>
+promise_test(async () => {
+  async function clickOn(element) {
+    const rect = element.getBoundingClientRect();
+    const actions = new test_driver.Actions()
+      .pointerMove(rect.left + rect.width / 2, rect.top + rect.height / 2)
+      .pointerDown()
+      .pointerUp();
+    await actions.send();
+  }
+
+  const div = document.querySelector('#ancestor');
+  const dialog = document.querySelector('dialog');
+  dialog.showModal();
+
+  const handledEvent = {};
+  document.addEventListener('click', function(event) {
+    handledEvent['document'] = true;
+  });
+
+  document.body.addEventListener('click', function(event) {
+    handledEvent['body'] = true;
+    // body should get a event only via bubbling.
+    if (event.target != dialog) {
+      assert_unreached('body was targeted for an click event');
+      div.style.backgroundColor = 'red';
+    }
+  });
+
+  div.addEventListener('click', function(event) {
+    handledEvent['div'] = true;
+    // div should get a event only via bubbling.
+    if (event.target != dialog) {
+      assert_unreached('div was targeted for an click event');
+      div.style.backgroundColor = 'red';
+    }
+  });
+
+  dialog.addEventListener('click', function(event) {
+    handledEvent['dialog'] = true;
+    dialog.style.backgroundColor = 'green';
+    if (event.target != dialog) {
+      assert_unreached('dialog was not targeted for a click event');
+      dialog.style.backgroundColor = 'red';
+    }
+  });
+
+  const nodes = [ 'document', 'body', 'div', 'dialog' ];
+  nodes.map(function(node) { handledEvent[node] = false; });
+  await clickOn(div);
+  assert_true(handledEvent.document, 'Clicking on ancestor.');
+  assert_false(handledEvent.body, 'Clicking on ancestor.');
+  assert_false(handledEvent.dialog, 'Clicking on ancestor.');
+  assert_false(handledEvent.div, 'Clicking on ancestor.');
+  handledEvent.document = false;
+
+  await clickOn(dialog);
+  assert_true(handledEvent.document, 'Clicking on dialog.');
+  assert_true(handledEvent.body, 'Clicking on dialog.');
+  assert_true(handledEvent.dialog, 'Clicking on dialog.');
+  assert_true(handledEvent.div, 'Clicking on dialog.');
+}, 'Test that ancestors of modal dialog are inert.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
new file mode 100644
index 0000000..75eb451
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/modal-dialog-blocks-mouse-events.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=author href="mailto:falken@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element">
+<link rel=help href="https://bugs.webkit.org/show_bug.cgi?id=110952">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p>
+To test manually, move the mouse to the blue box, click, and then move the
+mouse outside. Then repeat for the red box. The test succeeds if both boxes
+turn green
+</p>
+
+<style>
+#inert-div {
+  height: 100px;
+  width: 100px;
+  background: blue;
+}
+
+dialog {
+  width: 100px;
+}
+
+dialog::backdrop {
+  display: none;
+}
+
+#dialog-div {
+  height: 100px;
+  width: 100px;
+  background: red;
+}
+</style>
+
+<div id="inert-div"></div>
+<dialog id="dialog">
+  <div id="dialog-div"></div>
+</dialog>
+
+<script>
+promise_test(async () => {
+  async function clickOn(element) {
+    const rect = element.getBoundingClientRect();
+    const actions = new test_driver.Actions()
+      .pointerMove(rect.left + rect.width / 2, rect.top + rect.height / 2)
+      .pointerDown()
+      .pointerUp()
+      .pointerMove(0, 0);
+    await actions.send();
+  }
+
+  dialog.showModal();
+
+  inertDivHandledEvent = false;
+  inertDiv = document.getElementById('inert-div');
+  eventFiredOnInertNode = function(event) {
+    inertDivHandledEvent = true;
+    inertDiv.style.backgroundColor = 'red';
+  };
+
+  events =  ['mousedown', 'mouseup', 'click', 'mousemove', 'mouseover', 'mouseout'];
+  dialogDiv = document.getElementById('dialog-div');
+  handledEvents = {};
+  handledEvents.dialogDiv = {};
+  eventFiredOnDialog = function(event) {
+    handledEvents.dialogDiv[event.type] = true;
+    if (Object.keys(handledEvents.dialogDiv).length == events.length)
+      dialogDiv.style.backgroundColor = 'green';
+  };
+
+  handledEvents.document = {};
+  expectedEventCountForDocument = events.length - 1; // document won't get 'mouseout'
+  eventFiredOnDocument = function(event) {
+    handledEvents.document[event.type] = true;
+    if (Object.keys(handledEvents.document).length == document.expectedEventCount && !inertDivHandledEvent) {
+      inertDiv.style.backgroundColor = 'green';
+    }
+  };
+
+  for (let i = 0; i < events.length; ++i) {
+    inertDiv.addEventListener(events[i], eventFiredOnInertNode);
+    dialogDiv.addEventListener(events[i], eventFiredOnDialog);
+    document.addEventListener(events[i], eventFiredOnDocument);
+  }
+
+  await clickOn(inertDiv);
+  assert_false(inertDivHandledEvent, 'Clicking on inert box');
+  assert_equals(Object.keys(handledEvents.document).length, expectedEventCountForDocument, 'Clicking on inert box');
+
+  await clickOn(dialogDiv);
+  assert_false(inertDivHandledEvent, 'Clicking on non-inert box');
+  assert_equals(Object.keys(handledEvents.dialogDiv).length, events.length, 'Clicking on non-inert box');
+}, 'Ensure that mouse events are not dispatched to an inert node.');
+</script>
diff --git a/third_party/blink/web_tests/html/dialog/inert-inlines-expected.txt b/third_party/blink/web_tests/html/dialog/inert-inlines-expected.txt
deleted file mode 100644
index 867d0ae..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-inlines-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Tests that inert inlines do not receive mouse events. To test manually, click on all the "Click me"s. The test fails if you see red.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-clicking on a
-PASS inertElementFiredOn is false
-PASS expectedTarget.firedOn is true
-clicking on button
-PASS inertElementFiredOn is false
-PASS expectedTarget.firedOn is true
-clicking on div
-PASS inertElementFiredOn is false
-PASS expectedTarget.firedOn is true
-clicking on span
-PASS inertElementFiredOn is false
-PASS expectedTarget.firedOn is true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Click me Click me
-Click meeee
-Click me
-Click meeee
diff --git a/third_party/blink/web_tests/html/dialog/inert-inlines.html b/third_party/blink/web_tests/html/dialog/inert-inlines.html
deleted file mode 100644
index e050ce0..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-inlines.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<style>
-dialog {
-    width: 50px;
-}
-</style>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-<a id="a" href="javascript:void(0)">Click me</a>
-<button id="button">Click me</button>
-<div id="div" style="background-color: blue; width: 50px; height: 50px">Click meeee</div>
-<span id="span">Click me</span>
-<div id="dialog-parent" style="width: 50px; height: 50px">
-    <span id="dialog-sibling">Click meeee</span>
-    <dialog></dialog>
-</div>
-<script>
-function clickOn(element)
-{
-    if (!window.eventSender)
-        return;
-
-    var absoluteTop = 0;
-    var absoluteLeft = 0;
-    for (var parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
-        absoluteLeft += parentNode.offsetLeft;
-        absoluteTop += parentNode.offsetTop;
-    }
-
-    var x = absoluteLeft + element.offsetWidth / 2;
-    var y = absoluteTop + element.offsetHeight / 2;
-    eventSender.mouseMoveTo(x, y);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-    eventSender.mouseMoveTo(0, 0);
-}
-
-function eventFiredOnInertElement(e) {
-    e.target.style.background = 'red';
-    inertElementFiredOn = true;
-}
-
-description('Tests that inert inlines do not receive mouse events. ' +
-            'To test manually, click on all the "Click me"s. The test ' +
-            'fails if you see red.');
-inertElements = ['a', 'button', 'div', 'span']
-inertElements.forEach(function(id) {
-    element = document.getElementById(id);
-    element.addEventListener('click', eventFiredOnInertElement);
-    element.addEventListener('mousemove', eventFiredOnInertElement);
-});
-
-document.addEventListener('click', function(e) {
-    document.firedOn = true;
-});
-
-document.getElementById('dialog-parent').addEventListener('click', function(e) {
-    e.target.firedOn = true;
-});
-
-document.querySelector('dialog').showModal();
-inertElements.forEach(function(id) {
-    expectedTarget = document;
-    if (id == 'dialog-sibling')
-        expectedTarget = document.getElementById('dialog-parent')
-    element = document.getElementById(id);
-    inertElementFiredOn = false;
-    expectedTarget.firedOn = false;
-    debug('clicking on ' + id);
-    clickOn(element);
-    shouldBeFalse('inertElementFiredOn');
-    shouldBeTrue('expectedTarget.firedOn');
-});
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/html/dialog/inert-label-focus-expected.txt b/third_party/blink/web_tests/html/dialog/inert-label-focus-expected.txt
deleted file mode 100644
index 9f360b66..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-label-focus-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Tests focusing of an inert label for a non-inert target. label.focus() should send focus to the target, but clicking the label should be the same as clicking on the document body.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Testing that calling focus() on label sends focus to its target.
-PASS document.activeElement is document.querySelector('#submit')
-Testing that clicking on the label sends focus to document.body.
-PASS document.activeElement is document.body
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Label for Submit
- 
diff --git a/third_party/blink/web_tests/html/dialog/inert-label-focus.html b/third_party/blink/web_tests/html/dialog/inert-label-focus.html
deleted file mode 100644
index 73fce09..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-label-focus.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<html>
-<script src="../../resources/js-test.js"></script>
-<label for="submit">Label for Submit</label>
-<dialog>
-    <input id="text" type="text">
-    <input id="submit" type="submit">
-</dialog>
-<script>
-function clickOn(element) {
-    if (!window.eventSender)
-        return;
-
-    var absoluteTop = 0;
-    var absoluteLeft = 0;
-    for (var parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
-        absoluteLeft += parentNode.offsetLeft;
-        absoluteTop += parentNode.offsetTop;
-    }
-
-    var x = absoluteLeft + element.offsetWidth / 2;
-    var y = absoluteTop + element.offsetHeight / 2;
-    eventSender.mouseMoveTo(x, y);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-    eventSender.mouseMoveTo(0, 0);
-}
-
-description('Tests focusing of an inert label for a non-inert target. label.focus() should send focus ' +
-            'to the target, but clicking the label should be the same as clicking on the document body.');
-
-document.querySelector('dialog').showModal();
-document.querySelector('#text').focus();
-
-debug('Testing that calling focus() on label sends focus to its target.');
-label = document.querySelector('label');
-label.focus();
-shouldBe('document.activeElement', "document.querySelector('#submit')");
-
-debug('Testing that clicking on the label sends focus to document.body.');
-clickOn(label);
-shouldBe('document.activeElement', 'document.body');
-</script>
-</html>
diff --git a/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable-expected.txt b/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable-expected.txt
deleted file mode 100644
index 5f825bbc..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test that inert nodes cannot be edited. The test passes if the only text you can edit is in the dialog.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS notEditable.textContent is oldValue
-PASS editable.textContent is not oldValue
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable.html b/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable.html
deleted file mode 100644
index 850171a7..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-node-is-uneditable.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-<span id="not-editable" contenteditable>I'm not editable.</span>
-<dialog>
-    <span id="editable" contenteditable>I'm editable.</span>
-</dialog>
-<script>
-function clickOn(element)
-{
-    if (!window.eventSender)
-        return;
-
-    var absoluteTop = 0;
-    var absoluteLeft = 0;
-    for (var parentNode = element; parentNode; parentNode = parentNode.offsetParent) {
-        absoluteLeft += parentNode.offsetLeft;
-        absoluteTop += parentNode.offsetTop;
-    }
-
-    var x = absoluteLeft + element.offsetWidth / 2;
-    var y = absoluteTop + element.offsetHeight / 2;
-    eventSender.mouseMoveTo(x, y);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-    eventSender.mouseMoveTo(0, 0);
-}
-
-function test()
-{
-    description('Test that inert nodes cannot be edited. The test passes if the only text you can edit is in the dialog.');
-
-    dialog = document.querySelector('dialog');
-    dialog.showModal();
-    notEditable = document.querySelector('#not-editable');
-    editable = document.querySelector('#editable');
-
-    if (!window.eventSender)
-        return;
-    clickOn(notEditable);
-    oldValue = notEditable.textContent;
-    eventSender.keyDown('a');
-    shouldBe('notEditable.textContent', 'oldValue');
-
-    clickOn(editable);
-    oldValue = editable.textContent;
-    eventSender.keyDown('a');
-    shouldNotBe('editable.textContent', 'oldValue');
-
-    notEditable.remove();
-    editable.remove();
-}
-
-test();
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable-expected.txt b/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable-expected.txt
deleted file mode 100644
index cb2f35916..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS window.getSelection().toString() is "I'm selectable."
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-Here is a text node you can't select.
-I'm selectable.
diff --git a/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable.html b/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable.html
deleted file mode 100644
index 848a70b..0000000
--- a/third_party/blink/web_tests/html/dialog/inert-node-is-unselectable.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-Here is a text node you can't select.
-<dialog>I'm selectable.</dialog>
-<script>
-description('Test that inert nodes cannot be selected. The test passes if the only text you can select is inside the dialog.');
-
-dialog = document.querySelector('dialog');
-dialog.showModal();
-document.execCommand('SelectAll');
-shouldBeEqualToString('window.getSelection().toString()', "I'm selectable.");
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt b/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt
deleted file mode 100644
index 1245060..0000000
--- a/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Test that ancestors of modal <dialog> are inert. To test manually, click the left box. There should be no change. Then click the right box. If both boxes turn green, the test passes.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Clicking on ancestor
-PASS handledEvent.document is true
-PASS handledEvent.body is false
-PASS handledEvent.dialog is false
-PASS handledEvent.div is false
-Clicking on dialog
-PASS handledEvent.document is true
-PASS handledEvent.body is true
-PASS handledEvent.dialog is true
-PASS handledEvent.div is true
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert.html b/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert.html
deleted file mode 100644
index ea0d9c6..0000000
--- a/third_party/blink/web_tests/html/dialog/modal-dialog-ancestor-is-inert.html
+++ /dev/null
@@ -1,111 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<style>
-#ancestor {
-    position: absolute;
-    height: 50px;
-    width: 50px;
-    top: 200px;
-    left: 100px;
-    border: 1px solid;
-}
-
-dialog {
-    height: 50px;
-    width: 50px;
-    top: 200px;
-    left: 200px;
-    margin: 0;
-}
-
-dialog::backdrop {
-    display: none;
-}
-</style>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-<div id="ancestor">
-    <dialog></dialog>
-</div>
-<script>
-function clickOn(element)
-{
-    var rect = element.getBoundingClientRect();
-    eventSender.mouseMoveTo(rect.left + rect.width / 2, rect.top + rect.height / 2);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-}
-
-// For manual testing, indicate success only if automatic testing would also
-// print success for all ancestor nodes.
-function turnDivGreenOnSuccess()
-{
-    if (handledEvent['document'] && handledEvent['body'] && handledEvent['div'])
-        div.style.backgroundColor = 'green';
-}
-
-description('Test that ancestors of modal &lt;dialog&gt; are inert. To test manually, ' +
-            'click the left box. There should be no change. Then click the right box. ' +
-            'If both boxes turn green, the test passes.');
-div = document.querySelector('#ancestor');
-dialog = document.querySelector('dialog');
-dialog.showModal();
-
-handledEvent = {};
-document.addEventListener('click', function(event) {
-    handledEvent['document'] = true;
-    turnDivGreenOnSuccess();
-});
-
-document.body.addEventListener('click', function(event) {
-    handledEvent['body'] = true;
-    turnDivGreenOnSuccess();
-    // body should get a event only via bubbling.
-    if (event.target != dialog) {
-       testFailed('body was targeted for an click event');
-       div.style.backgroundColor = 'red';
-    }
-});
-
-div.addEventListener('click', function(event) {
-    handledEvent['div'] = true;
-    turnDivGreenOnSuccess();
-    // div should get a event only via bubbling.
-    if (event.target != dialog) {
-       testFailed('div was targeted for an click event');
-       div.style.backgroundColor = 'red';
-    }
-});
-
-dialog.addEventListener('click', function(event) {
-    handledEvent['dialog'] = true;
-    dialog.style.backgroundColor = 'green';
-    if (event.target != dialog) {
-        testFailed('dialog was not targeted for a click event');
-        dialog.style.backgroundColor = 'red';
-    }
-});
-
-if (window.eventSender) {
-    nodes = [ 'document', 'body', 'div', 'dialog' ];
-    nodes.map(function(node) { handledEvent[node] = false; });
-    debug('Clicking on ancestor');
-    clickOn(div);
-    shouldBeTrue('handledEvent.document');
-    shouldBeFalse('handledEvent.body');
-    shouldBeFalse('handledEvent.dialog');
-    shouldBeFalse('handledEvent.div');
-    handledEvent.document = false;
-
-    debug('Clicking on dialog');
-    clickOn(dialog);
-    shouldBeTrue('handledEvent.document');
-    shouldBeTrue('handledEvent.body');
-    shouldBeTrue('handledEvent.dialog');
-    shouldBeTrue('handledEvent.div');
-}
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt b/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt
deleted file mode 100644
index 64f6953..0000000
--- a/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Test for bug 110952. Ensure that mouse events are not dispatched to an inert node. To test manually, move the mouse to the blue box, click, and then move the mouse outside. Then repeat for the red box. The test succeeds if both boxes turn green.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Clicking on inert box
-PASS inertDivHandledEvent is false
-PASS Object.keys(handledEvents.document).length is expectedEventCountForDocument
-Clicking on non-inert box
-PASS inertDivHandledEvent is false
-PASS Object.keys(handledEvents.dialogDiv).length is events.length
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events.html b/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events.html
deleted file mode 100644
index 3c6db4ab..0000000
--- a/third_party/blink/web_tests/html/dialog/modal-dialog-blocks-mouse-events.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<style>
-#inert-div {
-    height: 100px;
-    width: 100px;
-    background: blue;
-}
-
-dialog {
-    width: 100px;
-}
-
-dialog::backdrop {
-    display: none;
-}
-
-#dialog-div {
-    height: 100px;
-    width: 100px;
-    background: red;
-}
-</style>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-<div id="inert-div"></div>
-<dialog id="dialog">
-    <div id="dialog-div"></div>
-</dialog>
-<script>
-description('Test for bug 110952. Ensure that mouse events are not ' +
-            'dispatched to an inert node. To test manually, move the mouse ' +
-            'to the blue box, click, and then move the mouse outside. Then ' +
-            'repeat for the red box. The test succeeds if both boxes turn ' +
-            'green.');
-
-function clickOn(element)
-{
-    if (!window.eventSender)
-        return;
-    var rect = element.getBoundingClientRect();
-    eventSender.mouseMoveTo(rect.left + rect.width / 2, rect.top + rect.height / 2);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-    eventSender.mouseMoveTo(0, 0);
-}
-
-if (window.testRunner)
-    testRunner.dumpAsText();
-
-dialog.showModal();
-
-inertDivHandledEvent = false;
-inertDiv = document.getElementById('inert-div');
-eventFiredOnInertNode = function(event) {
-    inertDivHandledEvent = true;
-    inertDiv.style.backgroundColor = 'red';
-};
-
-events =  ['mousedown', 'mouseup', 'click', 'mousemove', 'mouseover', 'mouseout'];
-dialogDiv = document.getElementById('dialog-div');
-handledEvents = {};
-handledEvents.dialogDiv = {};
-eventFiredOnDialog = function(event) {
-    handledEvents.dialogDiv[event.type] = true;
-    if (Object.keys(handledEvents.dialogDiv).length == events.length)
-        dialogDiv.style.backgroundColor = 'green';
-};
-
-handledEvents.document = {};
-expectedEventCountForDocument = events.length - 1; // document won't get 'mouseout'
-eventFiredOnDocument = function(event) {
-    handledEvents.document[event.type] = true;
-    if (Object.keys(handledEvents.document).length == document.expectedEventCount && !inertDivHandledEvent)
-        inertDiv.style.backgroundColor = 'green';
-};
-
-for (var i = 0; i < events.length; ++i) {
-    inertDiv.addEventListener(events[i], eventFiredOnInertNode);
-    dialogDiv.addEventListener(events[i], eventFiredOnDialog);
-    document.addEventListener(events[i], eventFiredOnDocument);
-}
-
-debug('Clicking on inert box');
-clickOn(inertDiv);
-shouldBeFalse('inertDivHandledEvent');
-shouldBe('Object.keys(handledEvents.document).length', 'expectedEventCountForDocument');
-
-debug('Clicking on non-inert box');
-clickOn(dialogDiv);
-shouldBeFalse('inertDivHandledEvent');
-shouldBe('Object.keys(handledEvents.dialogDiv).length', 'events.length');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
index c704856..92e6b2e 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
@@ -1,4 +1,9 @@
 [INTERFACES]
+interface BarcodeDetector
+    static method getSupportedFormats
+    attribute @@toStringTag
+    method constructor
+    method detect
 interface Notification : EventTarget
     getter image
 [NAMESPACES]
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index f1259166..5d302eab 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -51,11 +51,6 @@
     attribute @@toStringTag
     method constructor
     method updateUI
-interface BarcodeDetector
-    static method getSupportedFormats
-    attribute @@toStringTag
-    method constructor
-    method detect
 interface Blob
     attribute @@toStringTag
     getter size
diff --git a/third_party/blink/web_tests/platform/mac/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
index d6e213f..6abc29e 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-platform-specific-service-worker-expected.txt
@@ -1,4 +1,9 @@
 [INTERFACES]
+interface BarcodeDetector
+    static method getSupportedFormats
+    attribute @@toStringTag
+    method constructor
+    method detect
 interface Notification : EventTarget
 [NAMESPACES]
 [GLOBAL OBJECT]
diff --git a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
index 038f2b1..5c8c872 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
@@ -4,6 +4,11 @@
 
 Starting worker: resources/global-interface-listing-worker.js
 [Worker] [INTERFACES]
+[Worker] interface BarcodeDetector
+[Worker]     static method getSupportedFormats
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method detect
 [Worker] interface Notification : EventTarget
 [Worker] [NAMESPACES]
 [Worker] [GLOBAL OBJECT]
diff --git a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
index 3af5376..fa72a06 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -3,6 +3,11 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 [INTERFACES]
+interface BarcodeDetector
+    static method getSupportedFormats
+    attribute @@toStringTag
+    method constructor
+    method detect
 interface Bluetooth : EventTarget
     attribute @@toStringTag
     method constructor
diff --git a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
index 034c681..802ec99 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/virtual/stable/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
@@ -4,6 +4,11 @@
 
 Starting worker: resources/global-interface-listing-worker.js
 [Worker] [INTERFACES]
+[Worker] interface BarcodeDetector
+[Worker]     static method getSupportedFormats
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method detect
 [Worker] interface Notification : EventTarget
 [Worker] [NAMESPACES]
 [Worker] [GLOBAL OBJECT]
diff --git a/third_party/blink/web_tests/resources/global-interface-listing.js b/third_party/blink/web_tests/resources/global-interface-listing.js
index 5b4d5aa..16fbc4e 100644
--- a/third_party/blink/web_tests/resources/global-interface-listing.js
+++ b/third_party/blink/web_tests/resources/global-interface-listing.js
@@ -118,6 +118,7 @@
 // platform-specific expectations files to a bare minimum to make updates in the
 // common (platform-neutral) case as simple as possible.
 var platformSpecificInterfaces = new Set([
+  'BarcodeDetector',
   'Bluetooth',
   'BluetoothCharacteristicProperties',
   'BluetoothDevice',
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md
deleted file mode 100644
index 5585380..0000000
--- a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Fenced Frames + ShadowDOM
-
-This directory contains `wpt_internal/fenced_frame/` test expectations that are
-specific to the ShadowDOM implementation of fenced frames. The tests are run with
-the flag --enable-features=FencedFrames:implementation\_type/shadow\_dom.
-
-See crbug.com/1123606.
-
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html
deleted file mode 100644
index 1df34f7b..0000000
--- a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/basic-expected.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-  <iframe
-      style="width: 100%; height: 300px; box-sizing: border-box"
-      srcdoc="<body style='margin: 0px'><div style='background: red; width: 20px; height: 20px'></div></body>"></iframe>
-</body>
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html
deleted file mode 100644
index 90f5cff..0000000
--- a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/fenced_frame/visibility-changed-expected.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-  <iframe
-      style="width: 100%; height: 300px; box-sizing: border-box"
-      srcdoc="<body style='margin: 0px'><div style='background: green; width: 20px; height: 20px'></div></body>"></iframe>
-</body>
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt b/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt
deleted file mode 100644
index 41b28d9..0000000
--- a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/prerender.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-PASS Fenced Frame must not load prerendered page.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index c6b5140..4948853 100644
--- a/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -51,11 +51,6 @@
     attribute @@toStringTag
     method constructor
     method updateUI
-interface BarcodeDetector
-    static method getSupportedFormats
-    attribute @@toStringTag
-    method constructor
-    method detect
 interface Blob
     attribute @@toStringTag
     getter size
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 81104fc6..afc3f4f3 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -79,11 +79,6 @@
 [Worker]     method match
 [Worker]     method matchAll
 [Worker]     setter onprogress
-[Worker] interface BarcodeDetector
-[Worker]     static method getSupportedFormats
-[Worker]     attribute @@toStringTag
-[Worker]     method constructor
-[Worker]     method detect
 [Worker] interface Blob
 [Worker]     attribute @@toStringTag
 [Worker]     getter size
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index e00d5173..8736c498 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -320,11 +320,6 @@
     attribute @@toStringTag
     getter visible
     method constructor
-interface BarcodeDetector
-    static method getSupportedFormats
-    attribute @@toStringTag
-    method constructor
-    method detect
 interface BaseAudioContext : EventTarget
     attribute @@toStringTag
     getter audioWorklet
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 29b0bb5..40607b6 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -44,11 +44,6 @@
 [Worker]     method match
 [Worker]     method matchAll
 [Worker]     setter onprogress
-[Worker] interface BarcodeDetector
-[Worker]     static method getSupportedFormats
-[Worker]     attribute @@toStringTag
-[Worker]     method constructor
-[Worker]     method detect
 [Worker] interface Blob
 [Worker]     attribute @@toStringTag
 [Worker]     getter size
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index e2a45fe..4779f44 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -562,6 +562,10 @@
     property src
     property type
     property width
+html element fencedframe
+    property height
+    property src
+    property width
 html element fieldset
     property checkValidity
     property disabled
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index a145ec4..cba8941 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -79,11 +79,6 @@
 [Worker]     method match
 [Worker]     method matchAll
 [Worker]     setter onprogress
-[Worker] interface BarcodeDetector
-[Worker]     static method getSupportedFormats
-[Worker]     attribute @@toStringTag
-[Worker]     method constructor
-[Worker]     method detect
 [Worker] interface Blob
 [Worker]     attribute @@toStringTag
 [Worker]     getter size
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index cf414348..4c6a2ed3 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -537,11 +537,6 @@
     attribute @@toStringTag
     getter visible
     method constructor
-interface BarcodeDetector
-    static method getSupportedFormats
-    attribute @@toStringTag
-    method constructor
-    method detect
 interface BaseAudioContext : EventTarget
     attribute @@toStringTag
     getter audioWorklet
@@ -3455,6 +3450,15 @@
     setter src
     setter type
     setter width
+interface HTMLFencedFrameElement : HTMLElement
+    attribute @@toStringTag
+    getter height
+    getter src
+    getter width
+    method constructor
+    setter height
+    setter src
+    setter width
 interface HTMLFieldSetElement : HTMLElement
     attribute @@toStringTag
     getter disabled
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
index 35dc755..bf64d64f 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-dedicated-worker-expected.txt
@@ -4,6 +4,11 @@
 
 Starting worker: resources/global-interface-listing-worker.js
 [Worker] [INTERFACES]
+[Worker] interface BarcodeDetector
+[Worker]     static method getSupportedFormats
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method detect
 [Worker] interface Notification : EventTarget
 [Worker]     getter image
 [Worker] [NAMESPACES]
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-expected.txt
index 0e40791..c4d6a2e2 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-expected.txt
@@ -3,6 +3,11 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 [INTERFACES]
+interface BarcodeDetector
+    static method getSupportedFormats
+    attribute @@toStringTag
+    method constructor
+    method detect
 interface Bluetooth : EventTarget
     attribute @@toStringTag
     getter onadvertisementreceived
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
index be8425d..639c7c6 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-platform-specific-shared-worker-expected.txt
@@ -4,6 +4,11 @@
 
 Starting worker: resources/global-interface-listing-worker.js
 [Worker] [INTERFACES]
+[Worker] interface BarcodeDetector
+[Worker]     static method getSupportedFormats
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
+[Worker]     method detect
 [Worker] interface Notification : EventTarget
 [Worker]     getter image
 [Worker] [NAMESPACES]
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index d31cc4f..63854ad 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -44,11 +44,6 @@
 [Worker]     method match
 [Worker]     method matchAll
 [Worker]     setter onprogress
-[Worker] interface BarcodeDetector
-[Worker]     static method getSupportedFormats
-[Worker]     attribute @@toStringTag
-[Worker]     method constructor
-[Worker]     method detect
 [Worker] interface Blob
 [Worker]     attribute @@toStringTag
 [Worker]     getter size
diff --git a/third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/navigate-by-name-expected.txt b/third_party/blink/web_tests/wpt_internal/fenced_frame/navigate-by-name-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame/navigate-by-name-expected.txt
rename to third_party/blink/web_tests/wpt_internal/fenced_frame/navigate-by-name-expected.txt
diff --git a/tools/aggregation_service/aggregation_service_tool.cc b/tools/aggregation_service/aggregation_service_tool.cc
index eb20d83..c4326bb3 100644
--- a/tools/aggregation_service/aggregation_service_tool.cc
+++ b/tools/aggregation_service/aggregation_service_tool.cc
@@ -31,8 +31,8 @@
 
 absl::optional<content::TestAggregationService::Operation> ConvertToOperation(
     const std::string& operation_string) {
-  if (operation_string == "hierarchical-histogram")
-    return content::TestAggregationService::Operation::kHierarchicalHistogram;
+  if (operation_string == "histogram")
+    return content::TestAggregationService::Operation::kHistogram;
 
   return absl::nullopt;
 }
diff --git a/tools/aggregation_service/aggregation_service_tool_main.cc b/tools/aggregation_service/aggregation_service_tool_main.cc
index ae417a0..e208e63 100644
--- a/tools/aggregation_service/aggregation_service_tool_main.cc
+++ b/tools/aggregation_service/aggregation_service_tool_main.cc
@@ -47,9 +47,8 @@
   [--additional-fields=<additional_fields>]
 
   Examples:
-  aggregation_service_tool --operation="hierarchical-histogram" --bucket=1234
-  --value=5 --processing-type="two-party"
-  --reporting-origin="https://example.com"
+  aggregation_service_tool --operation="histogram" --bucket=1234 --value=5
+  --processing-type="two-party" --reporting-origin="https://example.com"
   --privacy-budget-key="test_privacy_budget_key"
   --helper-keys="https://a.com=keys1.json,https://b.com=keys2.json"
   --output-file="output.json"
@@ -70,8 +69,8 @@
   serialization. `scheduled_report_time` will be default to 30 seconds later.
 
   Switches:
-  --operation = Optional switch. Currently only supports
-                "hierarchical-histogram". Default is "hierarchical-histogram".
+  --operation = Optional switch. Currently only supports "histogram". Default is
+                "histogram".
   --bucket = Bucket key of the histogram contribution, must be non-negative
              integer.
   --value = Bucket value of the histogram contribution, must be non-negative
@@ -193,7 +192,7 @@
   std::string operation =
       command_line.HasSwitch(kSwitchOperation)
           ? command_line.GetSwitchValueASCII(kSwitchOperation)
-          : "hierarchical-histogram";
+          : "histogram";
 
   std::string processing_type =
       command_line.HasSwitch(kSwitchProcessingType)
diff --git a/tools/grit/preprocess_if_expr.py b/tools/grit/preprocess_if_expr.py
index b201b1f3..0046680 100644
--- a/tools/grit/preprocess_if_expr.py
+++ b/tools/grit/preprocess_if_expr.py
@@ -95,6 +95,18 @@
         raise
     if os.path.exists(out_path) and os.path.samefile(out_path, in_path):
       os.remove(out_path)
+
+    # Detect and delete any stale TypeScript files present in the output folder,
+    # corresponding to input .js files, since they can get picked up by
+    # subsequent ts_library() invocations and cause transient build failures.
+    # This can happen when a file is migrated from JS to TS  and a bot is
+    # switched from building a later CL to building an earlier CL.
+    [pathname, extension] = os.path.splitext(out_path)
+    if extension == '.js':
+      to_check = pathname + '.ts'
+      if os.path.exists(to_check):
+        os.remove(to_check)
+
     with io.open(out_path, mode='wb') as f:
       f.write(preprocessed.encode('utf-8'))
 
diff --git a/tools/mac/power/benchmark.py b/tools/mac/power/benchmark.py
index 808be0d..2c138df0 100755
--- a/tools/mac/power/benchmark.py
+++ b/tools/mac/power/benchmark.py
@@ -46,11 +46,7 @@
       action='store',
       choices=["wakeups", "cpu_time"],
       help="Profile the application in one of two modes: wakeups, cpu_time.")
-  parser.add_argument("--power_sampler",
-                      help="Path to power sampler binary",
-                      default=os.path.join(
-                          utils.FindChromiumSrcDir(os.path.dirname(__file__)),
-                          "out", "Default", "power_sampler"))
+  parser.add_argument("--power_sampler", help="Path to power sampler binary")
   parser.add_argument(
       '--scenarios',
       dest='scenarios',
diff --git a/tools/mac/power/export_dtrace.py b/tools/mac/power/export_dtrace.py
index f17dc6d..5ea9601 100755
--- a/tools/mac/power/export_dtrace.py
+++ b/tools/mac/power/export_dtrace.py
@@ -7,6 +7,7 @@
 from collections import defaultdict
 import argparse
 import itertools
+import json
 import logging
 import os
 import re
@@ -70,6 +71,9 @@
     line.function_id = function_id
     return function_id
 
+  def AddComment(self, comment: str):
+    self._profile.comment.append(self.GetStringId(comment))
+
   def AddSampleType(self, type: str, unit: str):
     """
     Adds a sample type that describe stackframes in this profile. See
@@ -292,8 +296,7 @@
                       help="Collapsed stack file.",
                       required=True)
   parser.add_argument("--output",
-                      help="The file to write the collapsed stacks into.",
-                      required=True)
+                      help="The file to write the collapsed stacks into.")
   parser.add_argument('--format',
                       dest='format',
                       action='store',
@@ -304,6 +307,7 @@
                       action='store_true',
                       help="Shorten stacks by removing.")
   args = parser.parse_args()
+  logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
 
   profile_mode = 'cpu_time'
   if 'wakeups' in args.stack_dir:
@@ -313,11 +317,27 @@
   if args.shorten:
     parser.ShortenStackSamples()
 
+  data_dir = os.path.abspath(os.path.join(args.stack_dir, os.pardir))
+  metadata_path = os.path.join(data_dir, "metadata.json")
+  if not os.path.isfile(metadata_path):
+    logging.error(f"Could not find metadata.json.")
+    sys.exit(-1)
+  with open(metadata_path, 'r') as metadata_file:
+    metadata = json.load(metadata_file)
+
+  output_filename = args.output
   if args.format == "pprof":
     profile_builder = ProfileBuilder()
+    profile_builder.AddComment(json.dumps(metadata, indent=2))
+    profile_builder.AddComment(f"Profile mode: {profile_mode}")
     parser.ConvertToPprof(profile_builder)
-
-    with open(args.output_filename, "wb") as output_file:
+    if output_filename is None:
+      output_filename = os.path.join(data_dir, f"profile_{profile_mode}.pb")
+    with open(output_filename, "wb") as output_file:
       output_file.write(profile_builder.SerializeToString())
   else:
-    parser.ConvertToCollapse(args.output_filename)
+    if output_filename is None:
+      output_filename = os.path.join(data_dir,
+                                     f"profile_{profile_mode}.collapsed")
+    parser.ConvertToCollapse(output_filename)
+  logging.info(f'Outputing profile in {os.path.abspath(output_filename)}')
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index c74bebb..2e1c0545 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1721,8 +1721,8 @@
     # under Xvfb on Linux.
     # TODO(tonikitoo,msisov,fwang): Find a way to run tests for the Wayland
     # backend.
-    use_xvfb = (self.platform == 'linux2' and not is_android and not is_fuchsia
-                and not is_cros_device)
+    use_xvfb = (self.platform.startswith('linux') and not is_android
+                and not is_fuchsia and not is_cros_device)
 
     asan = 'is_asan=true' in vals['gn_args']
     msan = 'is_msan=true' in vals['gn_args']
diff --git a/tools/mb/mb_unittest.py b/tools/mb/mb_unittest.py
index d89a031..935ba62 100755
--- a/tools/mb/mb_unittest.py
+++ b/tools/mb/mb_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2020 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.
@@ -48,7 +48,7 @@
       self.default_config = '/fake_src/tools/mb/mb_config.pyl'
       self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl'
       self.executable = '/usr/bin/python'
-      self.platform = 'linux2'
+      self.platform = 'linux'
       self.sep = '/'
       self.cwd = '/fake_src/out/Default'
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index d95027a..5ea41f31 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -5530,6 +5530,16 @@
   </description>
 </action>
 
+<action name="ContentSuggestions.Feed.WillRefresh">
+  <owner>adamta@google.org</owner>
+  <owner>sczs@chromium.org</owner>
+  <owner>feed@chromium.org</owner>
+  <description>
+    The feed will trigger a refresh, either manually by the user or because of
+    the cache expiring.
+  </description>
+</action>
+
 <action name="ContentSuggestions.NotificationsPreferenceOff">
   <owner>dgn@chromium.org</owner>
   <owner>finkm@chromium.org</owner>
@@ -19577,6 +19587,16 @@
   </description>
 </action>
 
+<action name="NewTabPage.ViewHierarchyFixed">
+  <owner>adamta@google.org</owner>
+  <owner>sczs@chromium.org</owner>
+  <owner>feed@chromium.org</owner>
+  <description>
+    Recorded when the NTP detects a broken view hierarchy and repairs itself.
+    TODO(crbug.com/1262536): Remove this when issue is fixed.
+  </description>
+</action>
+
 <action name="NewTabPage_ContentSuggestions_ArticlesUsage"
     not_user_triggered="true">
   <owner>markusheintz@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 847e4e47..bec126f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -8270,7 +8270,6 @@
   <int value="254" label="RFH_CREATE_CHILD_FRAME_SANDBOX_FLAGS"/>
   <int value="255" label="RFPH_FOCUSED_FENCED_FRAME"/>
   <int value="256" label="WCI_REQUEST_LOCK_MOUSE_FENCED_FRAME"/>
-  <int value="257" label="RFH_FENCED_FRAME_MOJO_WHEN_DISABLED"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
@@ -30398,6 +30397,7 @@
   <int value="1601" label="AUTOTESTPRIVATE_GETDISPLAYSMOOTHNESS"/>
   <int value="1602" label="AUTOTESTPRIVATE_RESETHOLDINGSPACE"/>
   <int value="1603" label="FILEMANAGERPRIVATEINTERNAL_GETDISALLOWEDTRANSFERS"/>
+  <int value="1604" label="WMDESKSPRIVATE_GETDESKTEMPLATEJSON"/>
 </enum>
 
 <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index c18fc29b..e95474d1 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -13,6 +13,7 @@
 csharrison@chromium.org
 cthomp@chromium.org
 curranmax@chromium.org
+davidmunro@google.com
 dewittj@chromium.org
 dmurph@chromium.org
 drubery@chromium.org
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 61b286cc..45bbb05 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -1439,7 +1439,7 @@
 </histogram>
 
 <histogram name="Apps.AppListBubbleCreationTime" units="ms"
-    expires_after="2021-12-31">
+    expires_after="2022-04-17">
   <owner>wcwang@chromium.org</owner>
   <owner>chromeos-launcher@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index 2f2bb2c0..a796910 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -349,9 +349,9 @@
 </histogram>
 
 <histogram name="Power.CpuTimeSecondsPerPowerMode.{ProcessType}"
-    enum="PowerMode" expires_after="2021-12-31">
+    enum="PowerMode" expires_after="2022-06-30">
   <owner>eseckler@chromium.org</owner>
-  <owner>skyostil@chromium.org</owner>
+  <owner>oksamyt@chromium.org</owner>
   <summary>
     Total seconds of CPU time consumed by Chrome's {ProcessType} process, split
     by power mode. Currently only implemented on Android. For every second of
diff --git a/ui/file_manager/file_manager/common/js/util_unittest.m.js b/ui/file_manager/file_manager/common/js/util_unittest.m.js
index 44698a2..f67269d7 100644
--- a/ui/file_manager/file_manager/common/js/util_unittest.m.js
+++ b/ui/file_manager/file_manager/common/js/util_unittest.m.js
@@ -8,6 +8,7 @@
 
 import {EntryList, FakeEntryImpl, VolumeEntry} from './files_app_entry_types.js';
 import {MockFileSystem} from './mock_entry.js';
+import {LEGACY_FILES_APP_URL, SWA_FILES_APP_URL} from './url_constants.js';
 import {util} from './util.js';
 import {VolumeManagerCommon} from './volume_manager_types.js';
 
@@ -353,11 +354,11 @@
   assertEquals(util.extractFilePath(undefined), null);
 
   // In the Extension:
-  url =
-      'filesystem:chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/external/Downloads-u/Downloads/f.zip';
-  assertEquals(util.extractFilePath(url), '/Downloads-u/Downloads/f.zip');
+  const zipPath = '/Downloads-u/Downloads/f.zip';
+  url = `filesystem:${LEGACY_FILES_APP_URL}external${zipPath}`;
+  assertEquals(util.extractFilePath(url), zipPath);
 
   // In the SWA:
-  url = 'filesystem:chrome://file-manager/external/Downloads-u/Downloads/f.zip';
-  assertEquals(util.extractFilePath(url), '/Downloads-u/Downloads/f.zip');
+  url = `filesystem:${SWA_FILES_APP_URL}external${zipPath}`;
+  assertEquals(util.extractFilePath(url), zipPath);
 }
diff --git a/ui/views/animation/OWNERS b/ui/views/animation/OWNERS
index 5bbbaff..b9be49e1 100644
--- a/ui/views/animation/OWNERS
+++ b/ui/views/animation/OWNERS
@@ -1 +1,4 @@
 per-file *ink*=pbos@chromium.org
+per-file animation_builder*=elainechien@chromium.org
+per-file animation_sequence_block*=elainechien@chromium.org
+per-file animation_abort_handle*=elainechien@chromium.org
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc
index fd3f24f..a87b20d 100644
--- a/ui/views/controls/webview/webview.cc
+++ b/ui/views/controls/webview/webview.cc
@@ -450,7 +450,7 @@
   }
 
   if (!contents) {
-    content::WebContents::CreateParams create_params(browser_context, nullptr,
+    content::WebContents::CreateParams create_params(browser_context,
                                                      creator_location);
     return content::WebContents::Create(create_params);
   }
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h
index 70415e1..041bcb7 100644
--- a/ui/views/controls/webview/webview.h
+++ b/ui/views/controls/webview/webview.h
@@ -18,6 +18,7 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/webview/webview_export.h"
+#include "ui/views/metadata/view_factory.h"
 #include "ui/views/view.h"
 
 namespace views {
@@ -204,6 +205,20 @@
   gfx::Size max_size_;
 };
 
+BEGIN_VIEW_BUILDER(WEBVIEW_EXPORT, WebView, View)
+VIEW_BUILDER_PROPERTY(content::BrowserContext*, BrowserContext)
+VIEW_BUILDER_PROPERTY(content::WebContents*, WebContents)
+VIEW_BUILDER_PROPERTY(bool, FastResize)
+VIEW_BUILDER_METHOD(EnableSizingFromWebContents,
+                    const gfx::Size&,
+                    const gfx::Size&)
+VIEW_BUILDER_PROPERTY(View*, CrashedOverlayView)
+VIEW_BUILDER_METHOD(set_is_primary_web_contents_for_window, bool)
+VIEW_BUILDER_METHOD(set_allow_accelerators, bool)
+END_VIEW_BUILDER
+
 }  // namespace views
 
+DEFINE_VIEW_BUILDER(WEBVIEW_EXPORT, WebView)
+
 #endif  // UI_VIEWS_CONTROLS_WEBVIEW_WEBVIEW_H_
diff --git a/ui/webui/resources/css/BUILD.gn b/ui/webui/resources/css/BUILD.gn
index 17e006f8..22e2aaf 100644
--- a/ui/webui/resources/css/BUILD.gn
+++ b/ui/webui/resources/css/BUILD.gn
@@ -51,4 +51,8 @@
   if (include_polymer) {
     in_files += [ "md_colors.css" ]
   }
+
+  if (is_chromeos) {
+    in_files += [ "os_header.css" ]
+  }
 }
diff --git a/ui/webui/resources/css/os_header.css b/ui/webui/resources/css/os_header.css
new file mode 100644
index 0000000..02f237f
--- /dev/null
+++ b/ui/webui/resources/css/os_header.css
@@ -0,0 +1,41 @@
+/* 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. */
+
+.os-link-container-container {
+  margin: 20px;
+}
+
+.os-link-container {
+  align-items: center;
+  /* color_primary GRAY 200 */
+  border: 1px solid rgb(232, 234, 237);
+  font-family: 'Roboto', Roboto, sans-serif;
+  border-radius: 4px;
+  display: flex;
+  font-size: 14px;
+  line-height: 20px;
+  margin-bottom: 28px;
+  padding: 12px;
+}
+
+@media (prefers-color-scheme: dark) {
+  .os-link-container {
+    /* color_primary GRAY 900 */
+    border: 1px solid rgb(32, 33, 36);
+  }
+}
+
+.os-link-icon {
+  background-image: url(chrome://resources/images/os_flags_app_icon.svg);
+  background-size: 16px 16px;
+  height: 16px;
+  margin-inline-end: 16px;
+  width: 16px;
+}
+
+#os-link-href {
+  font-size: 14px;
+  line-height: 20px;
+  padding-inline-start: 4px;
+}
diff --git a/ui/webui/resources/images/BUILD.gn b/ui/webui/resources/images/BUILD.gn
index 78a966e1..a9b455da 100644
--- a/ui/webui/resources/images/BUILD.gn
+++ b/ui/webui/resources/images/BUILD.gn
@@ -65,5 +65,10 @@
       "icon_visibility.svg",
     ]
   }
+
+  if (is_chromeos) {
+    input_files += [ "os_flags_app_icon.svg" ]
+  }
+
   resource_path_prefix = "images"
 }
diff --git a/components/flags_ui/resources/os_flags_app_icon.svg b/ui/webui/resources/images/os_flags_app_icon.svg
similarity index 100%
rename from components/flags_ui/resources/os_flags_app_icon.svg
rename to ui/webui/resources/images/os_flags_app_icon.svg
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 7c0943e..fdcdefd 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -81,6 +81,9 @@
       "post_message_api_server.js",
     ]
   }
+  if (is_chromeos) {
+    in_files += [ "os_about.js" ]
+  }
 
   # TODO(crbug.com/1184053): Fully remove once no longer used by CrOS.
   if (is_chromeos_ash) {
@@ -263,6 +266,10 @@
       "post_message_api_client.js",
     ]
   }
+  if (is_chromeos) {
+    input_files += [ "os_about.js" ]
+  }
+
   namespace_rewrites = [
     "cr.search_highlight_utils.Range|Range",
     "Polymer.ArraySplice.calculateSplices|calculateSplices",
diff --git a/ui/webui/resources/js/os_about.js b/ui/webui/resources/js/os_about.js
new file mode 100644
index 0000000..6955dbc6
--- /dev/null
+++ b/ui/webui/resources/js/os_about.js
@@ -0,0 +1,11 @@
+// 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.
+
+import {$} from 'chrome://resources/js/util.m.js';
+
+$('os-link-href').onclick = crosUrlAboutRedirect;
+
+function crosUrlAboutRedirect() {
+  chrome.send('crosUrlAboutRedirect');
+}