diff --git a/DEPS b/DEPS
index c47e99d1..500daca 100644
--- a/DEPS
+++ b/DEPS
@@ -269,19 +269,19 @@
   # 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': '3dc829746aace492c8e5871ab6da905fb6c56a08',
+  'skia_revision': '1186b60fcfac987c2264d95f1a4a98018c718e19',
   # 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': '62f0387a92f1e5987dae85682c845c244a3b175a',
+  'v8_revision': 'a0762ead80f58826ff6e09e19bccdc0bd65fc84b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '40269f91c3ae0fd1a2f02e30aa5a38fe03cf08f6',
+  'angle_revision': '118a6484c8d008af595abe87ab91ee98f9006c4d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '3ce90c53592847ef69206569d46bfc217a3b7bc2',
+  'swiftshader_revision': '73679602bc0be54c3fba1caaf2141b05f19aa468',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -348,7 +348,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': 'f1dd54487ab166f8e11e81a784f09be2402d0929',
+  'devtools_frontend_revision': '5baff14b8c6a722d0cf426134c366c43f92c562a',
   # 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.
@@ -384,7 +384,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': '92244ec9d463a705d0ef3542223e13b523f08854',
+  'dawn_revision': 'e9ce8326b7843be34e8a31a5a54600cb924c83f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -745,7 +745,7 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '02c5d69c82143d3d7867362dce01088467bee107',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'd6f38aa83c84f31185b16b8f138cace09c8343f5',
       'condition': 'checkout_ios',
   },
 
@@ -765,7 +765,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2ff701bbbeacb133aad87956b58d97dac86cfc8d',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '31892cf6c20691aac39b9f17278e08ded01aa491',
       'condition': 'checkout_ios',
   },
 
@@ -914,7 +914,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'BYxYEZvLrXS8AvKaRJNfGUwJEjLslkbSYUdG0kDQ4p8C',
+          'version': 'ysPZmJ22H1d0s_hhsbD8Z_63nV2LPoUuz24Ll_Xk788C',
       },
     ],
     'condition': 'checkout_android',
@@ -1705,7 +1705,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '0dd7405d1bfa950a71572aeb77aa38aacef6653d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ab8c1e50a3b3a15258b23120995ab7845ca40929',
+    Var('webrtc_git') + '/src.git' + '@' + 'f4fcdf1ee1fd5752f2af13fe731f5638363722ef',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1778,7 +1778,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1ef4e54b5094d0c65477fab41c082c7cd16110a4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9b3abfc9a1e2ea583c82b857c612365e5d093d67',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index 7930451..86f8504 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -153,16 +153,6 @@
             _include_primary_support = true
           }
           secondary_native_lib_placeholders = [ "libdummy.so" ]
-          static_library_provider_use_secondary_abi = true
-        }
-
-        # http://crbug.com/1042107.
-        if (is_component_build) {
-          if (invoker.is_64_bit_browser) {
-            main_component_library = "libmonochrome_64.cr.so"
-          } else {
-            main_component_library = "libmonochrome.cr.so"
-          }
         }
       } else {
         native_lib_placeholders = [ "libdummy.so" ]
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a48e53c..ea8e3541 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -3302,6 +3302,8 @@
     "test/ash_test_helper.h",
     "test/ash_test_suite.cc",
     "test/ash_test_suite.h",
+    "test/ash_test_util.cc",
+    "test/ash_test_util.h",
     "test/ash_test_views_delegate.cc",
     "test/ash_test_views_delegate.h",
     "test/fake_android_intent_helper.cc",
@@ -3445,6 +3447,7 @@
     "//ui/ozone",
     "//ui/platform_window/common",
     "//ui/shell_dialogs",
+    "//ui/snapshot:snapshot",
     "//ui/views",
     "//ui/views:test_support",
     "//ui/wm",
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc
index e02ec2a8..8d8cdcd 100644
--- a/ash/app_list/app_list_metrics.cc
+++ b/ash/app_list/app_list_metrics.cc
@@ -289,7 +289,7 @@
       // Ignore the OEM folder and the "Linux apps" folder because those folders
       // are automatically created. The following metrics are trying to measure
       // how often users engage with folders that they created themselves.
-      if (folder->IsPersistent())
+      if (folder->IsSystemFolder())
         continue;
       number_of_apps_in_non_system_folders += folder->item_list()->item_count();
       number_of_non_system_folders++;
diff --git a/ash/app_list/app_list_metrics_unittest.cc b/ash/app_list/app_list_metrics_unittest.cc
index 5b54a452..0ff07b8 100644
--- a/ash/app_list/app_list_metrics_unittest.cc
+++ b/ash/app_list/app_list_metrics_unittest.cc
@@ -900,12 +900,12 @@
                    "Apps.AppList.NumberOfAppsInNonSystemFolders", 2));
 }
 
-TEST_F(AppListPeriodicMetricsTest, RecordFolderMetrics_PersistentFolder) {
+TEST_F(AppListPeriodicMetricsTest, RecordFolderMetrics_SystemFolder) {
   base::HistogramTester histogram;
   AppListFolderItem* folder =
       GetAppListTestHelper()->model()->CreateSingleItemFolder("folder_id",
                                                               "item_id");
-  folder->SetIsPersistent(true);
+  folder->SetIsSystemFolder(true);
 
   RecordPeriodicAppListMetrics();
 
diff --git a/ash/app_list/model/app_list_folder_item.cc b/ash/app_list/model/app_list_folder_item.cc
index d73bed7f..63ab079 100644
--- a/ash/app_list/model/app_list_folder_item.cc
+++ b/ash/app_list/model/app_list_folder_item.cc
@@ -96,16 +96,16 @@
   UpdateIsNewInstall();
 }
 
-bool AppListFolderItem::IsPersistent() const {
-  return GetMetadata()->is_persistent;
+bool AppListFolderItem::IsSystemFolder() const {
+  return GetMetadata()->is_system_folder;
 }
 
-void AppListFolderItem::SetIsPersistent(bool is_persistent) {
-  metadata()->is_persistent = is_persistent;
+void AppListFolderItem::SetIsSystemFolder(bool is_system_folder) {
+  metadata()->is_system_folder = is_system_folder;
 }
 
 bool AppListFolderItem::ShouldAutoRemove() const {
-  return ChildItemCount() <= (IsPersistent() ? 0u : 1u);
+  return ChildItemCount() <= (IsSystemFolder() ? 0u : 1u);
 }
 
 std::string AppListFolderItem::GenerateId() {
diff --git a/ash/app_list/model/app_list_folder_item.h b/ash/app_list/model/app_list_folder_item.h
index 76afd20..1d14e04 100644
--- a/ash/app_list/model/app_list_folder_item.h
+++ b/ash/app_list/model/app_list_folder_item.h
@@ -92,9 +92,10 @@
   // AppListItemObserver:
   void ItemIsNewInstallChanged() override;
 
-  // Persistent folders will be retained even if there is 1 app in them.
-  bool IsPersistent() const;
-  void SetIsPersistent(bool is_persistent);
+  // Whether this is a system created folder like the Linux apps folder or the
+  // OEM folder.
+  bool IsSystemFolder() const;
+  void SetIsSystemFolder(bool is_system_folder);
 
   // Returns true if this folder is a candidate for auto-removal (based on its
   // type and the number of children it has).
diff --git a/ash/app_list/model/app_list_model_unittest.cc b/ash/app_list/model/app_list_model_unittest.cc
index 2b7a32b2..d66a8c8 100644
--- a/ash/app_list/model/app_list_model_unittest.cc
+++ b/ash/app_list/model/app_list_model_unittest.cc
@@ -582,12 +582,12 @@
   EXPECT_EQ("folder1", GetModelContents());
 }
 
-TEST_F(AppListModelFolderTest, UninstallPersistentFolderItem) {
+TEST_F(AppListModelFolderTest, UninstallSystemFolderItem) {
   AppListItem* item0 = model_->CreateAndAddItem("Item 0");
   AppListItem* item1 = model_->CreateAndAddItem("Item 1");
   AppListFolderItem* folder1 = static_cast<AppListFolderItem*>(
       model_->AddItem(new AppListFolderItem("folder1", model_.get())));
-  folder1->SetIsPersistent(true);
+  folder1->SetIsSystemFolder(true);
   EXPECT_EQ("Item 0,Item 1,folder1", GetModelContents());
 
   // Move all items to folder1.
diff --git a/ash/components/arc/ime/arc_ime_bridge_impl.cc b/ash/components/arc/ime/arc_ime_bridge_impl.cc
index 589d9e2..2bf6beb2 100644
--- a/ash/components/arc/ime/arc_ime_bridge_impl.cc
+++ b/ash/components/arc/ime/arc_ime_bridge_impl.cc
@@ -79,7 +79,8 @@
     return;
 
   ime_instance->SetCompositionText(base::UTF16ToUTF8(composition.text),
-                                   ConvertSegments(composition));
+                                   ConvertSegments(composition),
+                                   composition.selection);
 }
 
 void ArcImeBridgeImpl::SendConfirmCompositionText() {
diff --git a/ash/components/arc/mojom/ime.mojom b/ash/components/arc/mojom/ime.mojom
index 35343d3..0b688d88 100644
--- a/ash/components/arc/mojom/ime.mojom
+++ b/ash/components/arc/mojom/ime.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Next MinVersion: 21
+// Next MinVersion: 22
 
 module arc.mojom;
 
@@ -151,7 +151,10 @@
   [MinVersion=6] Init@6(pending_remote<ImeHost> host_remote) => ();
 
   // Sets composition text and attributes requested by the host IME.
-  SetCompositionText@1(string text, array<CompositionSegment> segments);
+  SetCompositionText@1(
+      string text,
+      array<CompositionSegment> segments,
+      [MinVersion=21] Range? selection_range);
 
   // Sets selection text and attributes requested by the host IME.
   [MinVersion=12] SetSelectionText@7(Range selection);
@@ -166,8 +169,8 @@
   // end of the text). If |new_cursor_position| <= 0, then it is relative to the
   // start (0 being the beginning of the text).
   InsertText@3(
-     string text,
-     [MinVersion=17] int32 new_cursor_position);
+      string text,
+      [MinVersion=17] int32 new_cursor_position);
 
   // Informs the virtual keyboard availability and bounds on screen is changing.
   // |is_available| whether a virtual keyboard is visible or not.
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 4945865..cdc00d80 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1433,7 +1433,7 @@
 // Uses new  AuthSession-based API in cryptohome to authenticate users during
 // sign-in.
 const base::Feature kUseAuthsessionAuthentication{
-    "UseAuthsessionAuthentication", base::FEATURE_ENABLED_BY_DEFAULT};
+    "UseAuthsessionAuthentication", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables using the BluetoothSystem Mojo interface for Bluetooth operations.
 const base::Feature kUseBluetoothSystemInAsh{"UseBluetoothSystemInAsh",
diff --git a/ash/display/screen_ash.cc b/ash/display/screen_ash.cc
index 6400703a..e3ead6a 100644
--- a/ash/display/screen_ash.cc
+++ b/ash/display/screen_ash.cc
@@ -42,6 +42,7 @@
       : display_list_(screen_ash->GetAllDisplays()),
         primary_display_(screen_ash->GetPrimaryDisplay()) {
     SetDisplayForNewWindows(primary_display_.id());
+    set_shutdown(true);
   }
 
   ScreenForShutdown(const ScreenForShutdown&) = delete;
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 6fe4657..2875eb15 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -131,8 +131,12 @@
   std::string folder_id;           // Id of folder where the item resides.
   syncer::StringOrdinal position;  // Position of the item.
   bool is_folder = false;          // Whether this item is a folder.
-  bool is_persistent = false;  // Whether this folder is allowed to contain only
-                               // 1 item.
+
+  // Whether the folder was system created (e.g. the OEM folder or Linux apps
+  // folder). Historically (pre-2022) these folders were the only ones allowed
+  // to contain a single item.
+  bool is_system_folder = false;
+
   gfx::ImageSkia icon;         // The icon of this item.
   bool is_page_break = false;  // Whether this item is a "page break" item.
   SkColor badge_color = SK_ColorWHITE;  // Notification badge color.
diff --git a/ash/shelf/shelf_context_menu_model_unittest.cc b/ash/shelf/shelf_context_menu_model_unittest.cc
index fc25400..0209e3435 100644
--- a/ash/shelf/shelf_context_menu_model_unittest.cc
+++ b/ash/shelf/shelf_context_menu_model_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "ash/shelf/shelf_context_menu_model.h"
 
+#include <tuple>
+
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
@@ -21,6 +23,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "components/user_manager/user_type.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display.h"
 #include "ui/views/widget/widget.h"
 
@@ -38,9 +41,13 @@
 
 class ShelfContextMenuModelTest
     : public AshTestBase,
-      public ::testing::WithParamInterface<user_manager::UserType> {
+      public ::testing::WithParamInterface<
+          std::tuple<user_manager::UserType, bool>> {
  public:
-  ShelfContextMenuModelTest() = default;
+  ShelfContextMenuModelTest() {
+    feature_list_.InitWithFeatureState(ash::features::kPersonalizationHub,
+                                       IsPersonalizationHubParamEnabled());
+  }
 
   ShelfContextMenuModelTest(const ShelfContextMenuModelTest&) = delete;
   ShelfContextMenuModelTest& operator=(const ShelfContextMenuModelTest&) =
@@ -58,7 +65,11 @@
     session->SwitchActiveUser(AccountId::FromUserEmail("user1@test.com"));
   }
 
-  user_manager::UserType GetUserType() const { return GetParam(); }
+  user_manager::UserType GetUserType() const { return std::get<0>(GetParam()); }
+
+  bool IsPersonalizationHubParamEnabled() const {
+    return std::get<1>(GetParam());
+  }
 
   MockNewWindowDelegate* GetMockNewWindowDelegate() {
     return static_cast<MockNewWindowDelegate*>(
@@ -66,6 +77,7 @@
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<TestNewWindowDelegateProvider> delegate_provider_;
 };
 
@@ -100,10 +112,12 @@
   int last_executed_command_ = 0;
 };
 
-INSTANTIATE_TEST_SUITE_P(,
-                         ShelfContextMenuModelTest,
-                         ::testing::Values(user_manager::USER_TYPE_REGULAR,
-                                           user_manager::USER_TYPE_CHILD));
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    ShelfContextMenuModelTest,
+    ::testing::Combine(::testing::Values(user_manager::USER_TYPE_REGULAR,
+                                         user_manager::USER_TYPE_CHILD),
+                       ::testing::Bool()));
 
 // Tests the default items in a shelf context menu.
 TEST_P(ShelfContextMenuModelTest, Basic) {
@@ -112,7 +126,11 @@
   ASSERT_EQ(3, menu.GetItemCount());
   EXPECT_EQ(CommandId::MENU_AUTO_HIDE, menu.GetCommandIdAt(0));
   EXPECT_EQ(CommandId::MENU_ALIGNMENT_MENU, menu.GetCommandIdAt(1));
-  EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(2));
+  if (IsPersonalizationHubParamEnabled()) {
+    EXPECT_EQ(CommandId::MENU_PERSONALIZATION_HUB, menu.GetCommandIdAt(2));
+  } else {
+    EXPECT_EQ(CommandId::MENU_CHANGE_WALLPAPER, menu.GetCommandIdAt(2));
+  }
   for (int i = 0; i < menu.GetItemCount(); ++i) {
     EXPECT_TRUE(menu.IsEnabledAt(i));
     EXPECT_TRUE(menu.IsVisibleAt(i));
@@ -162,32 +180,21 @@
 TEST_P(ShelfContextMenuModelTest, OpensPersonalizationHubOrWallpaper) {
   int64_t display_id = GetPrimaryDisplay().id();
 
-  base::test::ScopedFeatureList scoped_feature_list;
-  // Disable personalization hub feature should open wallpaper.
-  {
-    scoped_feature_list.InitAndDisableFeature(
-        ash::features::kPersonalizationHub);
+  ShelfContextMenuModel menu(nullptr, display_id);
+
+  if (IsPersonalizationHubParamEnabled()) {
+    // Personalization hub feature enabled should open hub.
+    EXPECT_CALL(*GetMockNewWindowDelegate(), OpenPersonalizationHub).Times(1);
+    menu.ActivatedAt(2);
+  } else {
     TestWallpaperControllerClient client;
     Shell::Get()->wallpaper_controller()->SetClient(&client);
     EXPECT_EQ(0u, client.open_count());
 
-    ShelfContextMenuModel menu_without_feature(nullptr, display_id);
     // Click the third option, wallpaper picker. It should open.
-    menu_without_feature.ActivatedAt(2);
+    menu.ActivatedAt(2);
     EXPECT_EQ(1u, client.open_count());
   }
-
-  scoped_feature_list.Reset();
-  // Enable personalization hub feature should open hub.
-  {
-    EXPECT_CALL(*GetMockNewWindowDelegate(), OpenPersonalizationHub).Times(1);
-
-    scoped_feature_list.InitAndEnableFeature(
-        ash::features::kPersonalizationHub);
-
-    ShelfContextMenuModel menu_with_feature(nullptr, display_id);
-    menu_with_feature.ActivatedAt(2);
-  }
 }
 
 // Tests custom items in a shelf context menu for an application.
@@ -249,8 +256,13 @@
   ShelfContextMenuModel menu1(nullptr, primary_id);
   EXPECT_EQ(2, menu1.GetItemCount());
   EXPECT_EQ(ShelfContextMenuModel::MENU_AUTO_HIDE, menu1.GetCommandIdAt(0));
-  EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
-            menu1.GetCommandIdAt(1));
+  if (IsPersonalizationHubParamEnabled()) {
+    EXPECT_EQ(ShelfContextMenuModel::MENU_PERSONALIZATION_HUB,
+              menu1.GetCommandIdAt(1));
+  } else {
+    EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
+              menu1.GetCommandIdAt(1));
+  }
 
   // Test that a menu shown out of tablet mode includes all three options:
   // MENU_AUTO_HIDE, MENU_ALIGNMENT_MENU, and MENU_CHANGE_WALLPAPER.
@@ -282,8 +294,13 @@
   EXPECT_TRUE(submenu->IsEnabledAt(2));
 
   // Test the wallpaper picker option.
-  EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
-            menu2.GetCommandIdAt(2));
+  if (IsPersonalizationHubParamEnabled()) {
+    EXPECT_EQ(ShelfContextMenuModel::MENU_PERSONALIZATION_HUB,
+              menu2.GetCommandIdAt(2));
+  } else {
+    EXPECT_EQ(ShelfContextMenuModel::MENU_CHANGE_WALLPAPER,
+              menu2.GetCommandIdAt(2));
+  }
   EXPECT_TRUE(menu2.IsEnabledAt(2));
 }
 
diff --git a/ash/test/ash_test_util.cc b/ash/test/ash_test_util.cc
new file mode 100644
index 0000000..fdd74337
--- /dev/null
+++ b/ash/test/ash_test_util.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/test/ash_test_util.h"
+
+#include "ash/shell.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/run_loop.h"
+#include "ui/gfx/image/image.h"
+#include "ui/snapshot/snapshot_aura.h"
+
+namespace ash::test {
+
+namespace {
+void SnapshotCallback(base::RunLoop* run_loop,
+                      gfx::Image* ret_image,
+                      gfx::Image image) {
+  *ret_image = image;
+  run_loop->Quit();
+}
+}  // namespace
+
+bool TakePrimaryDisplayScreenshotAndSave(const base::FilePath& file_path) {
+  // Return false if the file extension is not "png".
+  if (file_path.Extension() != ".png")
+    return false;
+
+  // Return false if `file_path`'s directory does not exist.
+  const base::FilePath directory_name = file_path.DirName();
+  if (!base::PathExists(directory_name))
+    return false;
+
+  base::RunLoop run_loop;
+  gfx::Image image;
+  ui::GrabWindowSnapshotAsyncAura(
+      Shell::Get()->GetPrimaryRootWindow(),
+      Shell::Get()->GetPrimaryRootWindow()->bounds(),
+      base::BindOnce(&SnapshotCallback, &run_loop, &image));
+  run_loop.Run();
+  auto data = image.As1xPNGBytes();
+  int data_size = static_cast<int>(data->size());
+  DCHECK_GT(data_size, 0);
+  int written_size = base::WriteFile(
+      file_path, reinterpret_cast<const char*>(data->front()), data_size);
+  return written_size == data_size;
+}
+
+}  // namespace ash::test
diff --git a/ash/test/ash_test_util.h b/ash/test/ash_test_util.h
new file mode 100644
index 0000000..4c59f41
--- /dev/null
+++ b/ash/test/ash_test_util.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_TEST_ASH_TEST_UTIL_H_
+#define ASH_TEST_ASH_TEST_UTIL_H_
+
+namespace base {
+class FilePath;
+}
+
+namespace ash::test {
+
+// Takes a screenshot of the primary display and saves the screenshot picture to
+// the location specified by `file_path`. Returns true if the screenshot is
+// taken and saved successfully. Useful for debugging ash unit tests. When using
+// this function on an ash unit test, the test code should be executed with
+// --enable-pixel-output-in-tests flag.
+// NOTE: `file_path` must end with the extension '.png'. If there is an existing
+// file matching `file_path`, the existing file will be overwritten.
+bool TakePrimaryDisplayScreenshotAndSave(const base::FilePath& file_path);
+
+}  // namespace ash::test
+
+#endif
diff --git a/ash/wallpaper/wallpaper_view.cc b/ash/wallpaper/wallpaper_view.cc
index b26e921..feabb247 100644
--- a/ash/wallpaper/wallpaper_view.cc
+++ b/ash/wallpaper/wallpaper_view.cc
@@ -54,8 +54,6 @@
     window->parent()->StackChildAtBottom(window);
     display::Display display =
         display::Screen::GetScreen()->GetDisplayNearestWindow(window);
-    display::ManagedDisplayInfo info =
-        Shell::Get()->display_manager()->GetDisplayInfo(display.id());
 
     for (auto* child : children()) {
       child->SetBounds(0, 0, display.size().width(), display.size().height());
diff --git a/ash/webui/media_app_ui/media_app_ui.cc b/ash/webui/media_app_ui/media_app_ui.cc
index 212ae7c..652ed10 100644
--- a/ash/webui/media_app_ui/media_app_ui.cc
+++ b/ash/webui/media_app_ui/media_app_ui.cc
@@ -107,14 +107,24 @@
   // File-type favicons.
   source->AddResourcePath("system_assets/pdf_icon.svg",
                           IDR_MEDIA_APP_PDF_ICON_SVG);
+  source->AddResourcePath("system_assets/pdf_icon_dark.svg",
+                          IDR_MEDIA_APP_PDF_ICON_DARK_SVG);
   source->AddResourcePath("system_assets/video_icon.svg",
                           IDR_MEDIA_APP_VIDEO_ICON_SVG);
+  source->AddResourcePath("system_assets/video_icon_dark.svg",
+                          IDR_MEDIA_APP_VIDEO_ICON_DARK_SVG);
   source->AddResourcePath("system_assets/image_icon.svg",
                           IDR_MEDIA_APP_IMAGE_ICON_SVG);
+  source->AddResourcePath("system_assets/image_icon_dark.svg",
+                          IDR_MEDIA_APP_IMAGE_ICON_DARK_SVG);
   source->AddResourcePath("system_assets/audio_icon.svg",
                           IDR_MEDIA_APP_AUDIO_ICON_SVG);
+  source->AddResourcePath("system_assets/audio_icon_dark.svg",
+                          IDR_MEDIA_APP_AUDIO_ICON_DARK_SVG);
   source->AddResourcePath("system_assets/file_icon.svg",
                           IDR_MEDIA_APP_FILE_ICON_SVG);
+  source->AddResourcePath("system_assets/file_icon_dark.svg",
+                          IDR_MEDIA_APP_FILE_ICON_DARK_SVG);
   return source;
 }
 
diff --git a/ash/webui/media_app_ui/resources/assets/audio_icon_dark.svg b/ash/webui/media_app_ui/resources/assets/audio_icon_dark.svg
new file mode 100644
index 0000000..944bbcf6
--- /dev/null
+++ b/ash/webui/media_app_ui/resources/assets/audio_icon_dark.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 15C16 15.55 15.55 16 15 16H14V13H16V15ZM6 16H5C4.45 16 4 15.55 4 15V13H6V16ZM2 9V15C2 16.66 3.34 18 5 18H8V11H4V9C4 5.13 6.13 4 10 4C13.87 4 16 5.13 16 9V11H12V18H15C16.66 18 18 16.66 18 15V9C18 4.03 14.97 2 10 2C5.03 2 2 4.03 2 9Z" fill="#e8eaed"/>
+</svg>
diff --git a/ash/webui/media_app_ui/resources/assets/file_icon_dark.svg b/ash/webui/media_app_ui/resources/assets/file_icon_dark.svg
new file mode 100644
index 0000000..2c23f14
--- /dev/null
+++ b/ash/webui/media_app_ui/resources/assets/file_icon_dark.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2C4.44772 2 4 2.44772 4 3V17C4 17.5523 4.44772 18 5 18H15C15.5523 18 16 17.5523 16 17V7L11 2H5ZM10 8V4H6V16H14V8H10Z" fill="#e8eaed"/>
+</svg>
diff --git a/ash/webui/media_app_ui/resources/assets/image_icon_dark.svg b/ash/webui/media_app_ui/resources/assets/image_icon_dark.svg
new file mode 100644
index 0000000..e01b954
--- /dev/null
+++ b/ash/webui/media_app_ui/resources/assets/image_icon_dark.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15 3H5C3.9 3 3 3.9 3 5V15C3 16.1 3.9 17 5 17H15C16.1 17 17 16.1 17 15V5C17 3.9 16.1 3 15 3ZM15 15H5V5H15V15ZM11.3333 9L9.5 12L8 10.6L6 14H14L11.3333 9Z" fill="#e8eaed"/>
+</svg>
diff --git a/ash/webui/media_app_ui/resources/assets/pdf_icon_dark.svg b/ash/webui/media_app_ui/resources/assets/pdf_icon_dark.svg
new file mode 100644
index 0000000..853adaf
--- /dev/null
+++ b/ash/webui/media_app_ui/resources/assets/pdf_icon_dark.svg
@@ -0,0 +1,6 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6 8.5C6 8.48274 6.00087 8.46569 6.00258 8.44888C6.02819 8.19675 6.24112 8 6.5 8H7.9C8.17614 8 8.4 8.22386 8.4 8.5V9.9C8.4 10.1761 8.17614 10.4 7.9 10.4H6.8V12H6V8.5ZM6.8 8.80005V9.60005H7.59999V8.80005H6.8Z" fill="#e8eaed"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.29999 8C9.02385 8 8.79999 8.22386 8.79999 8.5V12H10.7C10.9761 12 11.2 11.7761 11.2 11.5V8.5C11.2 8.22386 10.9761 8 10.7 8H9.29999ZM10.4 8.80005H9.59998V11.2H10.4V8.80005Z" fill="#e8eaed"/>
+<path d="M11.6 8.4C11.6 8.17909 11.7791 8 12 8H14V8.8H12.4V9.59998H13.2V10.4H12.4V12H11.6L11.6 8.4Z" fill="#e8eaed"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15 3H5C3.9 3 3 3.9 3 5V15C3 16.1 3.9 17 5 17H15C16.1 17 17 16.1 17 15V5C17 3.9 16.1 3 15 3ZM15 15H5V5H15V15Z" fill="#e8eaed"/>
+</svg>
diff --git a/ash/webui/media_app_ui/resources/assets/video_icon_dark.svg b/ash/webui/media_app_ui/resources/assets/video_icon_dark.svg
new file mode 100644
index 0000000..b3fb465
--- /dev/null
+++ b/ash/webui/media_app_ui/resources/assets/video_icon_dark.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 4H14L15 7H13L12 4H10L11 7H9L8 4H6L7 7H5L4 4C3 4 2 5.007 2 6V15C2 16 2.9 17 4 17H16C17.1 17.048 18 16.125 18 15V6C18 5.007 17 4 16 4ZM16 15H4V9H16V15Z" fill="#e8eaed"/>
+</svg>
diff --git a/ash/webui/media_app_ui/resources/js/launch.js b/ash/webui/media_app_ui/resources/js/launch.js
index cd3d9460..0e65514d 100644
--- a/ash/webui/media_app_ui/resources/js/launch.js
+++ b/ash/webui/media_app_ui/resources/js/launch.js
@@ -13,6 +13,7 @@
 import {DeleteFileMessage, FileContext, IsFileBrowserWritableMessage, LoadFilesMessage, Message, NavigateMessage, NotifyCurrentFileMessage, OpenAllowedFileMessage, OpenAllowedFileResponse, OpenFilesWithPickerMessage, OverwriteFileMessage, OverwriteViaFilePickerResponse, RenameFileMessage, RenameResult, RequestSaveFileMessage, RequestSaveFileResponse, SaveAsMessage, SaveAsResponse} from './message_types.js';
 import {mediaAppPageHandler} from './mojo_api_bootstrap.js';
 
+const DEFAULT_APP_ICON = 'app';
 const EMPTY_WRITE_ERROR_NAME = 'EmptyWriteError';
 
 // Open file picker configurations. Should be kept in sync with launch handler
@@ -164,6 +165,29 @@
 // event listener before the <iframe> is added to the DOM.
 guestMessagePipe.registerHandler(Message.IFRAME_READY, () => {});
 
+/**
+ * The type of icon to show for this app's window.
+ * @type {string}
+ */
+let appIconType = DEFAULT_APP_ICON;
+
+/**
+ * Sets the app icon depending on the icon type and color theme.
+ * @param {!MediaQueryList|!Event<!{matches: boolean}>}
+ *     mediaQueryList Determines whether or not the icon should be in dark mode.
+ */
+function updateAppIcon(mediaQueryList) {
+  // The default app icon does not have a separate dark variant.
+  const isDark =
+      mediaQueryList.matches && appIconType !== DEFAULT_APP_ICON ? '_dark' : '';
+
+  const icon = /** @type {!HTMLLinkElement} */ (
+      document.querySelector('link[rel=icon]'));
+  icon.href = `system_assets/${appIconType}_icon${isDark}.svg`;
+}
+
+const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+
 guestMessagePipe.registerHandler(Message.NOTIFY_CURRENT_FILE, message => {
   const notifyMsg = /** @type {!NotifyCurrentFileMessage} */ (message);
 
@@ -172,19 +196,19 @@
   appTitle = appTitle || title.text;
   title.text = notifyMsg.name || appTitle;
 
-  let genericType = notifyMsg.type ? notifyMsg.type.split('/')[0] : 'file';
+  appIconType = notifyMsg.type ? notifyMsg.type.split('/')[0] : 'file';
   if (title.text === appTitle) {
-    genericType = 'app';
+    appIconType = DEFAULT_APP_ICON;
   } else if (notifyMsg.type === 'application/pdf') {
-    genericType = 'pdf';
-  } else if (!['audio', 'image', 'video', 'file'].includes(genericType)) {
-    genericType = 'file';
+    appIconType = 'pdf';
+  } else if (!['audio', 'image', 'video', 'file'].includes(appIconType)) {
+    appIconType = 'file';
   }
-  const icon = /** @type {!HTMLLinkElement} */ (
-      document.querySelector('link[rel=icon]'));
-  icon.href = `system_assets/${genericType}_icon.svg`;
+  updateAppIcon(darkMediaQuery);
 });
 
+darkMediaQuery.addEventListener('change', updateAppIcon);
+
 guestMessagePipe.registerHandler(Message.OPEN_FEEDBACK_DIALOG, () => {
   let response = mediaAppPageHandler.openFeedbackDialog();
   if (response === null) {
diff --git a/ash/webui/media_app_ui/resources/media_app_resources.grd b/ash/webui/media_app_ui/resources/media_app_resources.grd
index 7b6147a..a14082b 100644
--- a/ash/webui/media_app_ui/resources/media_app_resources.grd
+++ b/ash/webui/media_app_ui/resources/media_app_resources.grd
@@ -17,10 +17,15 @@
       <include name="IDR_MEDIA_APP_VIEWPDFHOST_JS" file="js/viewpdfhost.js" type="BINDATA" />
       <include name="IDR_MEDIA_APP_APP_ICON_SVG" file="assets/app_icon.svg" type="BINDATA" />
       <include name="IDR_MEDIA_APP_IMAGE_ICON_SVG" file="assets/image_icon.svg" type="BINDATA" />
+      <include name="IDR_MEDIA_APP_IMAGE_ICON_DARK_SVG" file="assets/image_icon_dark.svg" type="BINDATA" />
       <include name="IDR_MEDIA_APP_AUDIO_ICON_SVG" file="assets/audio_icon.svg" type="BINDATA" />
+      <include name="IDR_MEDIA_APP_AUDIO_ICON_DARK_SVG" file="assets/audio_icon_dark.svg" type="BINDATA" />
       <include name="IDR_MEDIA_APP_VIDEO_ICON_SVG" file="assets/video_icon.svg" type="BINDATA" />
+      <include name="IDR_MEDIA_APP_VIDEO_ICON_DARK_SVG" file="assets/video_icon_dark.svg" type="BINDATA" />
       <include name="IDR_MEDIA_APP_PDF_ICON_SVG" file="assets/pdf_icon.svg" type="BINDATA" />
+      <include name="IDR_MEDIA_APP_PDF_ICON_DARK_SVG" file="assets/pdf_icon_dark.svg" type="BINDATA" />
       <include name="IDR_MEDIA_APP_FILE_ICON_SVG" file="assets/file_icon.svg" type="BINDATA" />
+      <include name="IDR_MEDIA_APP_FILE_ICON_DARK_SVG" file="assets/file_icon_dark.svg" type="BINDATA" />
 
       <!-- TODO(b/141588875): Switch these to IDR_MEDIA_APP_APP_ICON_*_PNG in the internal media_app_bundle_resources.grd file (and add more icon resolutions) when the final icon is ready. -->
       <include name="IDR_MEDIA_APP_GALLERY_ICON_16_PNG" file="assets/icon16.png" type="BINDATA" />
diff --git a/ash/webui/media_app_ui/test/dom_testing_helpers.js b/ash/webui/media_app_ui/test/dom_testing_helpers.js
index 3d20965..c88f42a4 100644
--- a/ash/webui/media_app_ui/test/dom_testing_helpers.js
+++ b/ash/webui/media_app_ui/test/dom_testing_helpers.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/* eslint no-var: "off" */
+
 /**
  * @fileoverview Helpers for testing against DOM. For integration tests, this is
  * injected into an isolated world, so can't access objects in other scripts.
@@ -17,7 +19,7 @@
  * @param {!Array<string>=} shadowRootPath
  * @returns {!Promise<!HTMLElement|!ShadowRoot>}
  */
-async function getNextRoot(selectorMethod, shadowRootPath = []) {
+var getNextRoot = async (selectorMethod, shadowRootPath = []) => {
   /** @type {!HTMLElement|!ShadowRoot} */
   let parentNode = document.body;
   const parentQuery = shadowRootPath.shift();
@@ -29,7 +31,7 @@
     parentNode = element.shadowRoot;
   }
   return parentNode;
-}
+};
 
 /**
  * Runs a query selector once. Returns the Element if it's found, otherwise
@@ -39,14 +41,14 @@
  * @param {!Array<string>=} path
  * @return {!Promise<!Element|undefined>}
  */
-async function getNode(query, path = []) {
+var getNode = async (query, path = []) => {
   const parentElement = await getNextRoot(getNode, path);
   const existingElement = parentElement.querySelector(query);
   if (existingElement) {
     return Promise.resolve(existingElement);
   }
   return Promise.resolve(undefined);
-}
+};
 
 /**
  * Runs a query selector until it finds an element (repeated on each mutation).
@@ -62,7 +64,7 @@
  * @param {!Array<string>=} opt_path
  * @return {!Promise<!Element>}
  */
-async function waitForNode(query, opt_path) {
+var waitForNode = async (query, opt_path) => {
   const parentElement = await getNextRoot(waitForNode, opt_path);
   const existingElement = parentElement.querySelector(query);
   if (existingElement) {
@@ -80,7 +82,7 @@
     observer.observe(
         parentElement, {attributes: true, childList: true, subtree: true});
   });
-}
+};
 
 /**
  * Returns a promise that resolves when the passed node's child list is updated
@@ -88,7 +90,7 @@
  * @param {!Node} node
  * @return {!Promise}
  */
-function childListUpdate(node) {
+var childListUpdate = (node) => {
   return new Promise(resolve => {
     const observer = new MutationObserver(() => {
       resolve();
@@ -96,4 +98,4 @@
     });
     observer.observe(node, {childList: true});
   });
-}
+};
diff --git a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
index e7f462f..35b6e4d4 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_browsertest.js
@@ -409,9 +409,47 @@
 
     assertEquals(getTitle().innerText, expectedTitle);
     assertEquals(getIcon().href.includes(expectedIconType), true);
+    assertEquals(getIcon().href.includes('dark'), false);
   }
 };
 
+// Test that each file type has a corresponding dark icon.
+MediaAppUIBrowserTest.NotifyCurrentFileDark = async () => {
+  const imageFile = new File([], 'image.png', {type: 'image/png'});
+  const audioFile = new File([], 'audio.wav', {type: 'audio/wav'});
+  const videoFile = new File([], 'video.mp4', {type: 'video/mp4'});
+  const pdfFile = new File([], 'form.pdf', {type: 'application/pdf'});
+  const unknownFile = new File([], 'foo.xyz', {type: 'unknown/unknown'});
+
+  const TEST_CASES = [
+    {file: imageFile, expectedIconType: 'image'},
+    {file: audioFile, expectedIconType: 'audio'},
+    {file: videoFile, expectedIconType: 'video'},
+    {file: unknownFile, expectedIconType: 'file'},
+    {file: pdfFile, expectedIconType: 'pdf'},
+  ];
+  for (const {file, expectedIconType} of TEST_CASES) {
+    const name = file ? file.name : undefined;
+    const type = file ? file.type : undefined;
+    await sendTestMessage(
+        {simple: 'notifyCurrentFile', simpleArgs: {name, type}});
+
+    assertEquals(getIcon().href.includes(expectedIconType), true);
+    assertEquals(getIcon().href.includes('dark'), true);
+  }
+};
+
+// Test that the Gallery app icon does not have a dark variant.
+MediaAppUIBrowserTest.NotifyCurrentFileAppIconDark = async () => {
+  await sendTestMessage({
+    simple: 'notifyCurrentFile',
+    simpleArgs: {name: undefined, type: undefined}
+  });
+
+  assertEquals(getIcon().href.includes('app'), true);
+  assertEquals(getIcon().href.includes('dark'), false);
+};
+
 // Tests that we show error UX when trying to launch an unopenable file.
 MediaAppUIBrowserTest.LaunchUnopenableFile = async () => {
   const mockFileHandle =
diff --git a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
index 7ec12c9..36857683 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
@@ -67,6 +67,29 @@
 
 // js2gtest fixtures require var here (https://crbug.com/1033337).
 // eslint-disable-next-line no-var
+var MediaAppUIWithDarkLightModeDarkGtestBrowserTest =
+    class extends MediaAppUIGtestBrowserTest {
+  /** @override */
+  get featureList() {
+    return {
+      enabled: [
+        ...super.featureList.enabled,
+        'chromeos::features::kDarkLightMode',
+      ]
+    };
+  }
+
+  /** @override */
+  get testGenPreamble() {
+    return () => {
+      // Switch to dark mode.
+      GEN('ash::ColorProvider::Get()->SetDarkModeEnabledForTest(true);');
+    };
+  }
+};
+
+// js2gtest fixtures require var here (https://crbug.com/1033337).
+// eslint-disable-next-line no-var
 var MediaAppUIWithoutDarkLightModeGtestBrowserTest =
     class extends MediaAppUIGtestBrowserTest {
   /** @override */
@@ -132,6 +155,9 @@
             MediaAppUIWithDarkLightModeGtestBrowserTest))
         .testCaseBodies,
     ...(/** @type {{testCaseBodies: Object}} */ (
+            MediaAppUIWithDarkLightModeDarkGtestBrowserTest))
+        .testCaseBodies,
+    ...(/** @type {{testCaseBodies: Object}} */ (
             MediaAppUIWithoutDarkLightModeGtestBrowserTest))
         .testCaseBodies,
   };
@@ -186,6 +212,18 @@
   runMediaAppTest('NotifyCurrentFile');
 });
 
+TEST_F(
+    'MediaAppUIWithDarkLightModeDarkGtestBrowserTest', 'NotifyCurrentFileDark',
+    () => {
+      runMediaAppTest('NotifyCurrentFileDark');
+    });
+
+TEST_F(
+    'MediaAppUIWithDarkLightModeDarkGtestBrowserTest',
+    'NotifyCurrentFileAppIconDark', () => {
+      runMediaAppTest('NotifyCurrentFileAppIconDark');
+    });
+
 TEST_F('MediaAppUIGtestBrowserTest', 'LaunchUnopenableFile', () => {
   runMediaAppTest('LaunchUnopenableFile');
 });
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index 63b45418..0e48749 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -112,6 +112,8 @@
       {"avatarLabel", IDS_PERSONALIZATION_APP_AVATAR_LABEL},
       {"takeWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_TAKE_PHOTO},
       {"takeWebcamVideo", IDS_PERSONALIZATION_APP_AVATAR_TAKE_VIDEO},
+      {"webcamCaptureInProgress",
+       IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS},
       {"confirmWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_PHOTO},
       {"confirmWebcamVideo", IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_VIDEO},
       {"rejectWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_REJECT_PHOTO},
diff --git a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
index bb0cd240..1a9eeb55 100644
--- a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
+++ b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.html
@@ -52,7 +52,7 @@
     padding: 6px 16px;
   }
 
-  paper-spinner-lite {
+  #cameraFeedSpinner {
     bottom: 50%;
     left: 50%;
     position: absolute;
@@ -79,11 +79,23 @@
     gap: 8px;
     justify-content: center;
   }
+
+  #loadingButton {
+    /* This button is disabled for aria purposes but should look like a regular
+       primary button. */
+    --disabled-text-color: var(--cros-button-label-color-primary);
+  }
+
+  #loadingButtonSpinner {
+    --paper-spinner-color: var(--cros-button-label-color-primary);
+    height: 14px;
+    width: 14px;
+  }
 </style>
 <cr-dialog id="dialog" show-close-button show-on-attach>
   <div slot="body">
     <template is="dom-if" if="[[showLoading_(cameraStream_, previewBlobUrl_)]]">
-      <paper-spinner-lite active></paper-spinner-lite>
+      <paper-spinner-lite id="cameraFeedSpinner" active></paper-spinner-lite>
     </template>
     <template is="dom-if" if="[[showSvgMask_(cameraStream_, previewBlobUrl_)]]">
       <svg viewbox="0 0 100 100" id="svg" hidden="">
@@ -108,12 +120,18 @@
   <template is="dom-if" if="[[showFooter_(cameraStream_, previewBlobUrl_)]]">
     <div slot="footer">
       <template is="dom-if"
-          if="[[showCameraFeed_(cameraStream_, previewBlobUrl_)]]">
+          if="[[showTakePhotoButton_(cameraStream_, previewBlobUrl_, captureInProgress_)]]">
         <cr-button id="takePhoto" on-click="takePhoto_" class="primary">
           <iron-icon icon="[[getTakePhotoIcon_(mode)]]"></iron-icon>
           <span>[[getTakePhotoText_(mode)]]</span>
         </cr-button>
       </template>
+      <template is="dom-if" if="[[showLoadingSpinnerButton_(mode, cameraStream_, previewBlobUrl_, captureInProgress_)]]">
+        <cr-button id="loadingButton" class="primary" disabled>
+          <paper-spinner-lite id="loadingButtonSpinner" active></paper-spinner-lite>
+          <span>$i18n{webcamCaptureInProgress}</span>
+        </cr-button>
+      </template>
       <template is="dom-if" if="[[previewBlobUrl_]]">
         <cr-button id="confirmPhoto" on-click="confirmPhoto_" class="primary">
           <iron-icon icon="personalization:circle_checkmark"></iron-icon>
diff --git a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.ts b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.ts
index 0f4a49f..d9706154 100644
--- a/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/user/avatar_camera_element.ts
@@ -219,6 +219,15 @@
     return !!this.cameraStream_ && !this.previewBlobUrl_;
   }
 
+  private showTakePhotoButton_(): boolean {
+    return this.showCameraFeed_() && !this.captureInProgress_;
+  }
+
+  private showLoadingSpinnerButton_(): boolean {
+    return this.mode === AvatarCameraMode.VIDEO && this.showCameraFeed_() &&
+        this.captureInProgress_;
+  }
+
   private showFooter_(): boolean {
     return this.showCameraFeed_() || !!this.previewBlobUrl_;
   }
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index 01ef3da..d20da8c7 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
+#include "base/files/file_path.h"
 #include "base/logging.h"
 #include "chromeos/ash/components/dbus/rmad/rmad.pb.h"
 #include "chromeos/ash/components/dbus/rmad/rmad_client.h"
@@ -932,6 +933,19 @@
                                            std::move(callback)));
 }
 
+void ShimlessRmaService::SaveLog(SaveLogCallback callback) {
+  if (state_proto_.state_case() != rmad::RmadState::kRepairComplete) {
+    LOG(ERROR) << "SaveLog called from incorrect state "
+               << state_proto_.state_case();
+    std::move(callback).Run(base::FilePath(""),
+                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    return;
+  }
+  RmadClient::Get()->SaveLog(base::BindOnce(&ShimlessRmaService::OnSaveLog,
+                                            weak_ptr_factory_.GetWeakPtr(),
+                                            std::move(callback)));
+}
+
 void ShimlessRmaService::OnGetLog(GetLogCallback callback,
                                   absl::optional<rmad::GetLogReply> response) {
   if (!response) {
@@ -944,6 +958,20 @@
   std::move(callback).Run(response->log(), response->error());
 }
 
+void ShimlessRmaService::OnSaveLog(
+    SaveLogCallback callback,
+    absl::optional<rmad::SaveLogReply> response) {
+  if (!response) {
+    LOG(ERROR) << "Failed to call rmad::SaveLog";
+    std::move(callback).Run(base::FilePath(""),
+                            rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+    return;
+  }
+
+  std::move(callback).Run(base::FilePath(response->save_path()),
+                          response->error());
+}
+
 void ShimlessRmaService::GetPowerwashRequired(
     GetPowerwashRequiredCallback callback) {
   if (state_proto_.state_case() != rmad::RmadState::kRepairComplete) {
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.h b/ash/webui/shimless_rma/backend/shimless_rma_service.h
index 875ce32..e945ebe 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.h
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.h
@@ -134,6 +134,7 @@
       WriteProtectManuallyEnabledCallback callback) override;
 
   void GetLog(GetLogCallback callback) override;
+  void SaveLog(SaveLogCallback callback) override;
   void GetPowerwashRequired(GetPowerwashRequiredCallback callback) override;
   void LaunchDiagnostics() override;
   void EndRma(rmad::RepairCompleteState::ShutdownMethod shutdown_method,
@@ -206,6 +207,8 @@
       EndRmaCallback callback);
   void OnGetLog(GetLogCallback callback,
                 absl::optional<rmad::GetLogReply> response);
+  void OnSaveLog(SaveLogCallback callback,
+                 absl::optional<rmad::SaveLogReply> response);
 
   void OnOsUpdateStatusCallback(update_engine::Operation operation,
                                 double progress,
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
index fdae9594..71be12e 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "ash/webui/shimless_rma/backend/shimless_rma_delegate.h"
 #include "ash/webui/shimless_rma/mojom/shimless_rma.mojom.h"
 #include "base/callback_helpers.h"
+#include "base/files/file_path.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
@@ -2785,6 +2786,54 @@
   run_loop.RunUntilIdle();
 }
 
+TEST_F(ShimlessRmaServiceTest, SaveLog) {
+  const std::vector<rmad::GetStateReply> fake_states = {
+      CreateStateReply(rmad::RmadState::kRepairComplete, rmad::RMAD_ERROR_OK)};
+  fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
+  const std::unique_ptr<base::FilePath> expected_save_path =
+      std::make_unique<base::FilePath>(
+          FILE_PATH_LITERAL("log/save/path/for/testing"));
+
+  fake_rmad_client_()->SetSaveLogReply(expected_save_path->value(),
+                                       rmad::RMAD_ERROR_OK);
+  base::RunLoop run_loop;
+  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
+      [&](mojom::State state, bool can_cancel, bool can_go_back,
+          rmad::RmadErrorCode error) {
+        EXPECT_EQ(state, mojom::State::kRepairComplete);
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+  run_loop.RunUntilIdle();
+
+  shimless_rma_provider_->SaveLog(base::BindLambdaForTesting(
+      [&](const base::FilePath& save_path, rmad::RmadErrorCode error) {
+        EXPECT_EQ(save_path, *expected_save_path.get());
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+  run_loop.RunUntilIdle();
+}
+
+TEST_F(ShimlessRmaServiceTest, SaveLogWrongStateEmpty) {
+  const std::vector<rmad::GetStateReply> fake_states = {CreateStateReply(
+      rmad::RmadState::kDeviceDestination, rmad::RMAD_ERROR_OK)};
+  fake_rmad_client_()->SetFakeStateReplies(std::move(fake_states));
+  base::RunLoop run_loop;
+  shimless_rma_provider_->GetCurrentState(base::BindLambdaForTesting(
+      [&](mojom::State state, bool can_cancel, bool can_go_back,
+          rmad::RmadErrorCode error) {
+        EXPECT_EQ(state, mojom::State::kChooseDestination);
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_OK);
+      }));
+  run_loop.RunUntilIdle();
+
+  shimless_rma_provider_->SaveLog(base::BindLambdaForTesting(
+      [&](const base::FilePath& save_path, rmad::RmadErrorCode error) {
+        EXPECT_TRUE(save_path.empty());
+        EXPECT_EQ(error, rmad::RmadErrorCode::RMAD_ERROR_REQUEST_INVALID);
+      }));
+  run_loop.RunUntilIdle();
+}
+
 TEST_F(ShimlessRmaServiceTest, GetPowerwashRequired) {
   rmad::GetStateReply repair_complete_state =
       CreateStateReply(rmad::RmadState::kRepairComplete, rmad::RMAD_ERROR_OK);
diff --git a/ash/webui/shimless_rma/mojom/shimless_rma.mojom b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
index 8232bf9..e31bcbce 100644
--- a/ash/webui/shimless_rma/mojom/shimless_rma.mojom
+++ b/ash/webui/shimless_rma/mojom/shimless_rma.mojom
@@ -36,6 +36,7 @@
 
 module ash.shimless_rma.mojom;
 
+import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 
 // RMA states.
@@ -754,6 +755,9 @@
   // Get the RMA Log.
   // Returns an error indicating success or a failure.
   GetLog() => (string log, RmadErrorCode error);
+  // Save the RMA Log to the USB drive.
+  // Returns an error indicating success or a failure.
+  SaveLog() => (mojo_base.mojom.FilePath save_path, RmadErrorCode error);
   // Get whether need to powerwash at the end of repair.
   GetPowerwashRequired() => (bool powerwash_required);
   // Launch the system diagnostics app.
diff --git a/ash/webui/shimless_rma/resources/BUILD.gn b/ash/webui/shimless_rma/resources/BUILD.gn
index b873d80..8bcb68a 100644
--- a/ash/webui/shimless_rma/resources/BUILD.gn
+++ b/ash/webui/shimless_rma/resources/BUILD.gn
@@ -14,6 +14,7 @@
 preprocessed_dir = "preprocessed"
 preprocessed_gen_manifest = "preprocessed_gen_manifest.json"
 preprocessed_mojo_manifest = "preprocessed_mojo_manifest.json"
+preprocess_external_mojo_manifest = "preprocessed_external_mojo_manifest.json"
 
 polymer_element_files = [
   "base_page.js",
@@ -68,12 +69,17 @@
   ]
   input_files_base_dir = rebase_path(".", "//")
   deps = [
+    ":preprocess_external_mojo",
     ":preprocess_generated",
     ":preprocess_mojo",
   ]
   manifest_files = [
     "$target_gen_dir/$preprocessed_gen_manifest",
     "$target_gen_dir/$preprocessed_mojo_manifest",
+    "$target_gen_dir/$preprocess_external_mojo_manifest",
+  ]
+  resource_path_rewrites = [
+    "mojo/public/mojom/base/file_path.mojom-lite.js|file_path.mojom-lite.js",
   ]
   grd_prefix = "ash_shimless_rma"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
@@ -476,6 +482,14 @@
   in_files = [ "mojom/shimless_rma.mojom-lite.js" ]
 }
 
+preprocess_if_expr("preprocess_external_mojo") {
+  deps = [ "//mojo/public/mojom/base:base_js__generator" ]
+  in_folder = "$root_gen_dir"
+  out_folder = "$target_gen_dir/preprocessed_external_mojo"
+  out_manifest = "$target_gen_dir/$preprocess_external_mojo_manifest"
+  in_files = [ "mojo/public/mojom/base/file_path.mojom-lite.js" ]
+}
+
 html_to_js("web_components") {
   js_files = polymer_element_files
 }
diff --git a/ash/webui/shimless_rma/resources/fake_data.js b/ash/webui/shimless_rma/resources/fake_data.js
index 343ffd2..bc5fef5 100644
--- a/ash/webui/shimless_rma/resources/fake_data.js
+++ b/ash/webui/shimless_rma/resources/fake_data.js
@@ -445,3 +445,6 @@
     'tristique risus nec. Scelerisque eu ultrices vitae auctor eu augue ut ' +
     'lectus. Tellus pellentesque eu tincidunt tortor aliquam. Fermentum leo ' +
     'vel orci porta non pulvinar neque laoreet suspendisse.\n';
+
+/** @type {string} */
+export const fakeLogSavePath = 'fake/save/path';
diff --git a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
index 634eee25..32b9d96 100644
--- a/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/webui/shimless_rma/resources/fake_shimless_rma_service.js
@@ -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 '/file_path.mojom-lite.js';
+
 import {FakeMethodResolver} from 'chrome://resources/ash/common/fake_method_resolver.js';
 import {FakeObservables} from 'chrome://resources/ash/common/fake_observables.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
@@ -720,6 +722,20 @@
     this.methods_.setResult('getLog', {log: log, error: RmadErrorCode.kOk});
   }
 
+  /**
+   * @return {!Promise<{savePath: !mojoBase.mojom.FilePath, error:
+   *     !RmadErrorCode}>}
+   */
+  saveLog() {
+    return this.methods_.resolveMethod('saveLog');
+  }
+
+  /** @param {!mojoBase.mojom.FilePath} savePath */
+  setSaveLogResult(savePath) {
+    this.methods_.setResult(
+        'saveLog', {savePath: savePath, error: RmadErrorCode.kOk});
+  }
+
   /** @return {!Promise<{powerwashRequired: boolean, error: !RmadErrorCode}>} */
   getPowerwashRequired() {
     return this.methods_.resolveMethod('getPowerwashRequired');
@@ -1211,6 +1227,7 @@
     // undefined by default.
     this.components_ = [];
     this.setGetLogResult('');
+    this.setSaveLogResult({'path': ''});
   }
 
   /**
@@ -1292,6 +1309,7 @@
     this.methods_.register('writeProtectManuallyEnabled');
 
     this.methods_.register('getLog');
+    this.methods_.register('saveLog');
     this.methods_.register('getPowerwashRequired');
     this.methods_.register('endRma');
 
diff --git a/ash/webui/shimless_rma/resources/mojo_interface_provider.js b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
index 0a2aa9b..916a08c0 100644
--- a/ash/webui/shimless_rma/resources/mojo_interface_provider.js
+++ b/ash/webui/shimless_rma/resources/mojo_interface_provider.js
@@ -4,7 +4,7 @@
 
 import {assert} from 'chrome://resources/js/assert.m.js';
 
-import {fakeCalibrationComponentsWithFails, fakeChromeVersion, fakeComponents, fakeDeviceRegions, fakeDeviceSkus, fakeDeviceWhiteLabels, fakeLog, fakeRsuChallengeCode, fakeRsuChallengeQrCode, fakeStates} from './fake_data.js';
+import {fakeCalibrationComponentsWithFails, fakeChromeVersion, fakeComponents, fakeDeviceRegions, fakeDeviceSkus, fakeDeviceWhiteLabels, fakeLog, fakeLogSavePath, fakeRsuChallengeCode, fakeRsuChallengeQrCode, fakeStates} from './fake_data.js';
 import {FakeShimlessRmaService} from './fake_shimless_rma_service.js';
 import {CalibrationSetupInstruction, NetworkConfigServiceInterface, RmadErrorCode, ShimlessRmaService, ShimlessRmaServiceInterface, WriteProtectDisableCompleteAction} from './shimless_rma_types.js';
 
@@ -86,6 +86,7 @@
 
   service.automaticallyTriggerPowerCableStateObservation();
   service.setGetLogResult(fakeLog);
+  service.setSaveLogResult({'path': fakeLogSavePath});
   service.setGetPowerwashRequiredResult(true);
 
   // Set the fake service.
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_types.js b/ash/webui/shimless_rma/resources/shimless_rma_types.js
index ae3cfb0..e4b0c142 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_types.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma_types.js
@@ -7,12 +7,14 @@
  * Type aliases for the mojo API.
  */
 
-import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import './file_path.mojom-lite.js';
 import './mojom/shimless_rma.mojom-lite.js';
 
+import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
+
 /**
  * Return type from state progression methods.
  * Convenience type as mojo-lite does not define types for method results and
diff --git a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html
index 30e3be2..658fd952 100644
--- a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html
+++ b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.html
@@ -227,7 +227,8 @@
     <cr-button id="closeLogDialogButton" on-click="onCancelClick_">
       [[i18n('rmaLogsCancelButtonText')]]
     </cr-button>
-    <cr-button id="saveLogDialogButton" class="text-button action-button">
+    <cr-button id="saveLogDialogButton" class="text-button action-button"
+        on-click="onSaveLogClick_">
       [[i18n('rmaLogsSaveToUsbButtonText')]]
     </cr-button>
   </div>
diff --git a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.js b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.js
index 1743c34..b68576ff 100644
--- a/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.js
+++ b/ash/webui/shimless_rma/resources/wrapup_repair_complete_page.js
@@ -268,6 +268,11 @@
   }
 
   /** @protected */
+  onSaveLogClick_() {
+    this.shimlessRmaService_.saveLog();
+  }
+
+  /** @protected */
   onCutoffCancelClick_() {
     this.cancelBatteryCutoff_();
   }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index be46e9c6..caae724 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1019,11 +1019,11 @@
 
   if (is_win) {
     sources += [
-      "debug/close_handle_hook_win.cc",
-      "debug/close_handle_hook_win.h",
       "debug/debugger_win.cc",
       "debug/gdi_debug_util_win.cc",
       "debug/gdi_debug_util_win.h",
+      "debug/handle_hooks_win.cc",
+      "debug/handle_hooks_win.h",
       "debug/invalid_access_win.cc",
       "debug/invalid_access_win.h",
       "debug/stack_trace_win.cc",
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index f4b7965..f42cffc 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -7,6 +7,7 @@
 #include <array>
 #include <cstdint>
 #include <ostream>
+#include <string>
 
 #include "base/allocator/partition_allocator/address_pool_manager.h"
 #include "base/allocator/partition_allocator/page_allocator.h"
@@ -19,6 +20,10 @@
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 
+#if BUILDFLAG(IS_IOS)
+#include <mach-o/dyld.h>
+#endif
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
@@ -70,60 +75,105 @@
     PartitionAddressSpace::GigaCageSetup PartitionAddressSpace::setup_;
 
 #if defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
+#if BUILDFLAG(IS_IOS)
+namespace {
+bool IsIOSTestProcess() {
+  // On iOS, only applications with the extended virtual addressing entitlement
+  // can use a large address space. Since Earl Grey test runner apps cannot get
+  // entitlements, they must use a much smaller pool size.
+  uint32_t executable_length = 0;
+  _NSGetExecutablePath(NULL, &executable_length);
+  PA_DCHECK(executable_length > 0);
+
+  // 'new' cannot be used here, since this function is called during
+  // PartitionAddressSpace initialization, at which point 'new' interception
+  // is already active. 'malloc' is safe to use, since on Apple platforms,
+  // InitializeDefaultAllocatorPartitionRoot() is called before 'malloc'
+  // interception is set up.
+  char* executable_path = (char*)malloc(executable_length);
+  int rv = _NSGetExecutablePath(executable_path, &executable_length);
+  PA_DCHECK(!rv);
+  size_t executable_path_length =
+      std::char_traits<char>::length(executable_path);
+
+  const char kTestProcessSuffix[] = "Runner";
+  size_t test_process_suffix_length =
+      std::char_traits<char>::length(kTestProcessSuffix);
+
+  if (executable_path_length < test_process_suffix_length)
+    return false;
+
+  return !std::char_traits<char>::compare(
+      executable_path + (executable_path_length - test_process_suffix_length),
+      kTestProcessSuffix, test_process_suffix_length);
+}
+}  // namespace
+
+ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() {
+  return IsIOSTestProcess() ? kRegularPoolSizeForIOSTestProcess
+                            : kRegularPoolSize;
+}
+ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() {
+  return IsIOSTestProcess() ? kBRPPoolSizeForIOSTestProcess : kBRPPoolSize;
+}
+#else
 ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() {
   return kRegularPoolSize;
 }
 ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() {
   return kBRPPoolSize;
 }
+#endif  // BUILDFLAG(IS_IOS)
 #endif  // defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
 
 void PartitionAddressSpace::Init() {
   if (IsInitialized())
     return;
 
+  size_t regular_pool_size = RegularPoolSize();
   setup_.regular_pool_base_address_ = AllocPages(
-      RegularPoolSize(), RegularPoolSize(),
+      regular_pool_size, regular_pool_size,
       PageAccessibilityConfiguration::kInaccessible, PageTag::kPartitionAlloc);
   if (!setup_.regular_pool_base_address_)
     HandleGigaCageAllocFailure();
 #if defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
-  setup_.regular_pool_base_mask_ = ~(RegularPoolSize() - 1) & kMemTagUnmask;
+  setup_.regular_pool_base_mask_ = ~(regular_pool_size - 1) & kMemTagUnmask;
 #endif
-  PA_DCHECK(!(setup_.regular_pool_base_address_ & (RegularPoolSize() - 1)));
+  PA_DCHECK(!(setup_.regular_pool_base_address_ & (regular_pool_size - 1)));
   setup_.regular_pool_ = AddressPoolManager::GetInstance().Add(
-      setup_.regular_pool_base_address_, RegularPoolSize());
+      setup_.regular_pool_base_address_, regular_pool_size);
   PA_CHECK(setup_.regular_pool_ == kRegularPoolHandle);
   PA_DCHECK(!IsInRegularPool(setup_.regular_pool_base_address_ - 1));
   PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_));
   PA_DCHECK(IsInRegularPool(setup_.regular_pool_base_address_ +
-                            RegularPoolSize() - 1));
+                            regular_pool_size - 1));
   PA_DCHECK(
-      !IsInRegularPool(setup_.regular_pool_base_address_ + RegularPoolSize()));
+      !IsInRegularPool(setup_.regular_pool_base_address_ + regular_pool_size));
 
+  size_t brp_pool_size = BRPPoolSize();
   // Reserve an extra allocation granularity unit before the BRP pool, but keep
   // the pool aligned at BRPPoolSize(). A pointer immediately past an allocation
   // is a valid pointer, and having a "forbidden zone" before the BRP pool
   // prevents such a pointer from "sneaking into" the pool.
   const size_t kForbiddenZoneSize = PageAllocationGranularity();
   uintptr_t base_address = AllocPagesWithAlignOffset(
-      0, BRPPoolSize() + kForbiddenZoneSize, BRPPoolSize(),
-      BRPPoolSize() - kForbiddenZoneSize,
+      0, brp_pool_size + kForbiddenZoneSize, brp_pool_size,
+      brp_pool_size - kForbiddenZoneSize,
       PageAccessibilityConfiguration::kInaccessible, PageTag::kPartitionAlloc);
   if (!base_address)
     HandleGigaCageAllocFailure();
   setup_.brp_pool_base_address_ = base_address + kForbiddenZoneSize;
 #if defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
-  setup_.brp_pool_base_mask_ = ~(BRPPoolSize() - 1) & kMemTagUnmask;
+  setup_.brp_pool_base_mask_ = ~(brp_pool_size - 1) & kMemTagUnmask;
 #endif
-  PA_DCHECK(!(setup_.brp_pool_base_address_ & (BRPPoolSize() - 1)));
+  PA_DCHECK(!(setup_.brp_pool_base_address_ & (brp_pool_size - 1)));
   setup_.brp_pool_ = AddressPoolManager::GetInstance().Add(
-      setup_.brp_pool_base_address_, BRPPoolSize());
+      setup_.brp_pool_base_address_, brp_pool_size);
   PA_CHECK(setup_.brp_pool_ == kBRPPoolHandle);
   PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ - 1));
   PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_));
-  PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_ + BRPPoolSize() - 1));
-  PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ + BRPPoolSize()));
+  PA_DCHECK(IsInBRPPool(setup_.brp_pool_base_address_ + brp_pool_size - 1));
+  PA_DCHECK(!IsInBRPPool(setup_.brp_pool_base_address_ + brp_pool_size));
 
 #if PA_STARSCAN_USE_CARD_TABLE
   // Reserve memory for PCScan quarantine card table.
diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h
index 8719868..febb5037 100644
--- a/base/allocator/partition_allocator/partition_address_space.h
+++ b/base/allocator/partition_allocator/partition_address_space.h
@@ -201,18 +201,29 @@
   static constexpr size_t kBRPPoolSize = kPoolMaxSize;
   static_assert(base::bits::IsPowerOfTwo(kRegularPoolSize) &&
                 base::bits::IsPowerOfTwo(kBRPPoolSize));
-#if BUILDFLAG(IS_IOS)
-  // TODO(crbug.com/1250788): Remove the iOS special case.
-  static constexpr size_t kConfigurablePoolMaxSize = kPoolMaxSize;
-  static constexpr size_t kConfigurablePoolMinSize = kPoolMaxSize;
-#else
   static constexpr size_t kConfigurablePoolMaxSize = kPoolMaxSize;
   static constexpr size_t kConfigurablePoolMinSize = 1 * kGiB;
-#endif
   static_assert(kConfigurablePoolMinSize <= kConfigurablePoolMaxSize);
   static_assert(base::bits::IsPowerOfTwo(kConfigurablePoolMaxSize) &&
                 base::bits::IsPowerOfTwo(kConfigurablePoolMinSize));
 
+#if BUILDFLAG(IS_IOS)
+
+#if !defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
+#error iOS is only supported with a dynamically sized GigaCase.
+#endif
+
+  // We can't afford pool sizes as large as kPoolMaxSize in iOS EarlGrey tests,
+  // since the test process cannot use an extended virtual address space (see
+  // crbug.com/1250788).
+  static constexpr size_t kRegularPoolSizeForIOSTestProcess = kGiB / 4;
+  static constexpr size_t kBRPPoolSizeForIOSTestProcess = kGiB / 4;
+  static_assert(kRegularPoolSizeForIOSTestProcess < kRegularPoolSize);
+  static_assert(kBRPPoolSizeForIOSTestProcess < kBRPPoolSize);
+  static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForIOSTestProcess) &&
+                base::bits::IsPowerOfTwo(kBRPPoolSizeForIOSTestProcess));
+#endif  // BUILDFLAG(IOS_IOS)
+
 #if !defined(PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE)
   // Masks used to easy determine belonging to a pool.
   // On Arm, the top byte of each pointer is ignored (meaning there are
diff --git a/base/allocator/partition_allocator/partition_alloc_config.h b/base/allocator/partition_allocator/partition_alloc_config.h
index 1bc2462c..e2806f13 100644
--- a/base/allocator/partition_allocator/partition_alloc_config.h
+++ b/base/allocator/partition_allocator/partition_alloc_config.h
@@ -30,9 +30,11 @@
 #define PA_STARSCAN_NEON_SUPPORTED
 #endif
 
-#if 0
+#if BUILDFLAG(IS_IOS)
 // Use dynamically sized GigaCage. This allows to query the size at run-time,
-// before initialization, instead of using a hardcoded constexpr.
+// before initialization, instead of using a hardcoded constexpr. This is needed
+// on iOS because iOS test processes can't handle a large cage (see
+// crbug.com/1250788).
 #define PA_USE_DYNAMICALLY_SIZED_GIGA_CAGE
 #endif
 
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h
index 783f11e..5c4fc39 100644
--- a/base/allocator/partition_allocator/partition_alloc_constants.h
+++ b/base/allocator/partition_allocator/partition_alloc_constants.h
@@ -251,12 +251,7 @@
 #if defined(PA_HAS_64_BITS_POINTERS)
 // The Configurable Pool is only available in 64-bit mode
 constexpr size_t kNumPools = 3;
-// TODO(crbug.com/1250788): Remove the iOS special case, once larger address
-// space can be used there. This limitation isn't meant for releasing, but is ok
-// to keep for now only because nothing uses PartitionAlloc on iOS yet.
-#if BUILDFLAG(IS_IOS)
-constexpr size_t kPoolMaxSize = kGiB / 4;
-#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 // Special-case macOS. Contrary to other platforms, there is no sandbox limit
 // there, meaning that a single renderer could "happily" consume >8GiB. So the
 // 8GiB pool size is a regression. Make the limit higher on this platform only
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index e2f11102..5d75f3af 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -53,7 +53,7 @@
 #include <sys/time.h>
 #endif  // BUILDFLAG(IS_POSIX)
 
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE)
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_MAC)
 #include <OpenCL/opencl.h>
 
 #include <base/mac/mac_util.h>
diff --git a/base/debug/close_handle_hook_win.h b/base/debug/close_handle_hook_win.h
deleted file mode 100644
index c775d75..0000000
--- a/base/debug/close_handle_hook_win.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 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 BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
-#define BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
-
-#include "base/base_export.h"
-
-namespace base {
-namespace debug {
-
-// Installs the hooks required to debug use of improper handles.
-BASE_EXPORT void InstallHandleHooks();
-
-}  // namespace debug
-}  // namespace base
-
-#endif  // BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_
diff --git a/base/debug/close_handle_hook_win.cc b/base/debug/handle_hooks_win.cc
similarity index 61%
rename from base/debug/close_handle_hook_win.cc
rename to base/debug/handle_hooks_win.cc
index 68b0198..bc3eed4b 100644
--- a/base/debug/close_handle_hook_win.cc
+++ b/base/debug/handle_hooks_win.cc
@@ -2,17 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/debug/close_handle_hook_win.h"
+#include "base/debug/handle_hooks_win.h"
 
-#include <Windows.h>
+#include <windows.h>
+
 #include <psapi.h>
 #include <stddef.h>
 
-#include <algorithm>
-#include <memory>
-#include <vector>
-
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/win/iat_patch_function.h"
 #include "base/win/pe_image.h"
 #include "base/win/scoped_handle.h"
@@ -20,24 +19,18 @@
 
 namespace {
 
-typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle);
+using CloseHandleType = decltype(&::CloseHandle);
+using DuplicateHandleType = decltype(&::DuplicateHandle);
 
-typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process,
-                                           HANDLE source_handle,
-                                           HANDLE target_process,
-                                           HANDLE* target_handle,
-                                           DWORD desired_access,
-                                           BOOL inherit_handle,
-                                           DWORD options);
-
-CloseHandleType g_close_function = NULL;
-DuplicateHandleType g_duplicate_function = NULL;
+CloseHandleType g_close_function = nullptr;
+DuplicateHandleType g_duplicate_function = nullptr;
 
 // The entry point for CloseHandle interception. This function notifies the
 // verifier about the handle that is being closed, and calls the original
 // function.
 BOOL WINAPI CloseHandleHook(HANDLE handle) {
-  base::win::OnHandleBeingClosed(handle);
+  base::win::OnHandleBeingClosed(handle,
+                                 base::win::HandleOperation::kCloseHandleHook);
   return g_close_function(handle);
 }
 
@@ -50,7 +43,8 @@
                                 DWORD options) {
   if ((options & DUPLICATE_CLOSE_SOURCE) &&
       (GetProcessId(source_process) == ::GetCurrentProcessId())) {
-    base::win::OnHandleBeingClosed(source_handle);
+    base::win::OnHandleBeingClosed(
+        source_handle, base::win::HandleOperation::kDuplicateHandleHook);
   }
 
   return g_duplicate_function(source_process, source_handle, target_process,
@@ -74,9 +68,7 @@
   AutoProtectMemory(const AutoProtectMemory&) = delete;
   AutoProtectMemory& operator=(const AutoProtectMemory&) = delete;
 
-  ~AutoProtectMemory() {
-    RevertProtection();
-  }
+  ~AutoProtectMemory() { RevertProtection(); }
 
   // Grants write access to a given memory range.
   bool ChangeProtection(void* address, size_t bytes);
@@ -101,7 +93,7 @@
     return false;
 
   DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
-                        PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
+                         PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
                         memory_info.Protect;
 
   DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
@@ -128,9 +120,12 @@
   old_protect_ = 0;
 }
 
-// Performs an EAT interception.
-void EATPatch(HMODULE module, const char* function_name,
-              void* new_function, void** old_function) {
+#if defined(ARCH_CPU_32_BITS)
+// Performs an EAT interception. Only supported on 32-bit.
+void EATPatch(HMODULE module,
+              const char* function_name,
+              void* new_function,
+              void** old_function) {
   if (!module)
     return;
 
@@ -150,30 +145,35 @@
     return;
 
   // Perform the patch.
-  *eat_entry = static_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
-                                  reinterpret_cast<uintptr_t>(module));
+  *eat_entry =
+      base::checked_cast<DWORD>(reinterpret_cast<uintptr_t>(new_function) -
+                                reinterpret_cast<uintptr_t>(module));
 }
+#endif  // defined(ARCH_CPU_32_BITS)
 
 // Performs an IAT interception.
-base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name,
-                                      void* new_function, void** old_function) {
+std::unique_ptr<base::win::IATPatchFunction> IATPatch(HMODULE module,
+                                                      const char* function_name,
+                                                      void* new_function,
+                                                      void** old_function) {
   if (!module)
-    return NULL;
+    return nullptr;
 
-  base::win::IATPatchFunction* patch = new base::win::IATPatchFunction;
+  auto patch = std::make_unique<base::win::IATPatchFunction>();
   __try {
     // There is no guarantee that |module| is still loaded at this point.
     if (patch->PatchFromModule(module, "kernel32.dll", function_name,
                                new_function)) {
-      delete patch;
-      return NULL;
+      return nullptr;
     }
-  } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
-              GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
-              GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ?
-             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+  } __except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
+               GetExceptionCode() == EXCEPTION_GUARD_PAGE ||
+               GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR)
+                  ? EXCEPTION_EXECUTE_HANDLER
+                  : EXCEPTION_CONTINUE_SEARCH) {
     // Leak the patch.
-    return NULL;
+    std::ignore = patch.release();
+    return nullptr;
   }
 
   if (!(*old_function)) {
@@ -184,44 +184,32 @@
   return patch;
 }
 
-// Keeps track of all the hooks needed to intercept functions which could
-// possibly close handles.
-class HandleHooks {
- public:
-  HandleHooks() {}
+}  // namespace
 
-  HandleHooks(const HandleHooks&) = delete;
-  HandleHooks& operator=(const HandleHooks&) = delete;
-
-  ~HandleHooks() {}
-
-  void AddIATPatch(HMODULE module);
-  void AddEATPatch();
-
- private:
-  std::vector<base::win::IATPatchFunction*> hooks_;
-};
-
+// static
 void HandleHooks::AddIATPatch(HMODULE module) {
   if (!module)
     return;
 
-  base::win::IATPatchFunction* patch = NULL;
-  patch =
+  auto close_handle_patch =
       IATPatch(module, "CloseHandle", reinterpret_cast<void*>(&CloseHandleHook),
                reinterpret_cast<void**>(&g_close_function));
-  if (!patch)
+  if (!close_handle_patch)
     return;
-  hooks_.push_back(patch);
+  // This is intentionally leaked.
+  std::ignore = close_handle_patch.release();
 
-  patch = IATPatch(module, "DuplicateHandle",
-                   reinterpret_cast<void*>(&DuplicateHandleHook),
-                   reinterpret_cast<void**>(&g_duplicate_function));
-  if (!patch)
+  auto duplicate_handle_patch = IATPatch(
+      module, "DuplicateHandle", reinterpret_cast<void*>(&DuplicateHandleHook),
+      reinterpret_cast<void**>(&g_duplicate_function));
+  if (!duplicate_handle_patch)
     return;
-  hooks_.push_back(patch);
+  // This is intentionally leaked.
+  std::ignore = duplicate_handle_patch.release();
 }
 
+#if defined(ARCH_CPU_32_BITS)
+// static
 void HandleHooks::AddEATPatch() {
   // An attempt to restore the entry on the table at destruction is not safe.
   EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle",
@@ -231,33 +219,24 @@
            reinterpret_cast<void*>(&DuplicateHandleHook),
            reinterpret_cast<void**>(&g_duplicate_function));
 }
+#endif  // defined(ARCH_CPU_32_BITS)
 
-void PatchLoadedModules(HandleHooks* hooks) {
+// static
+void HandleHooks::PatchLoadedModules() {
   const DWORD kSize = 256;
   DWORD returned;
-  std::unique_ptr<HMODULE[]> modules(new HMODULE[kSize]);
-  if (!EnumProcessModules(GetCurrentProcess(), modules.get(),
-                          kSize * sizeof(HMODULE), &returned)) {
+  auto modules = std::make_unique<HMODULE[]>(kSize);
+  if (!::EnumProcessModules(GetCurrentProcess(), modules.get(),
+                            kSize * sizeof(HMODULE), &returned)) {
     return;
   }
   returned /= sizeof(HMODULE);
   returned = std::min(kSize, returned);
 
   for (DWORD current = 0; current < returned; current++) {
-    hooks->AddIATPatch(modules[current]);
+    AddIATPatch(modules[current]);
   }
 }
 
-}  // namespace
-
-void InstallHandleHooks() {
-  static HandleHooks* hooks = new HandleHooks();
-
-  // Performing EAT interception first is safer in the presence of other
-  // threads attempting to call CloseHandle.
-  hooks->AddEATPatch();
-  PatchLoadedModules(hooks);
-}
-
 }  // namespace debug
 }  // namespace base
diff --git a/base/debug/handle_hooks_win.h b/base/debug/handle_hooks_win.h
new file mode 100644
index 0000000..5459393
--- /dev/null
+++ b/base/debug/handle_hooks_win.h
@@ -0,0 +1,41 @@
+// Copyright 2015 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 BASE_DEBUG_HANDLE_HOOKS_WIN_H_
+#define BASE_DEBUG_HANDLE_HOOKS_WIN_H_
+
+#include "base/base_export.h"
+#include "base/win/windows_types.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace debug {
+
+// Provides the ability to intercept functions which could possibly close
+// handles in support of the handle tracker.
+// This is a currently a container class for static functions because there is
+// ongoing work to make the patches unhook, currently blocked by test failures.
+// See https://crbug.com/1327397.
+class BASE_EXPORT HandleHooks {
+ public:
+  HandleHooks() = delete;
+
+  HandleHooks(const HandleHooks&) = delete;
+  HandleHooks& operator=(const HandleHooks&) = delete;
+
+  // Patch IAT for a specified module.
+  static void AddIATPatch(HMODULE module);
+  // Add an EAT patch on kernel32.dll. This patch does not get removed. This is
+  // only supported on 32-bit because the EAT only supports 32-bit RVAs.
+#if defined(ARCH_CPU_32_BITS)
+  static void AddEATPatch();
+#endif
+  // Patch IAT for all currently loaded modules.
+  static void PatchLoadedModules();
+};
+
+}  // namespace debug
+}  // namespace base
+
+#endif  // BASE_DEBUG_HANDLE_HOOKS_WIN_H_
diff --git a/base/win/scoped_handle.cc b/base/win/scoped_handle.cc
index de68545..1c1cdce 100644
--- a/base/win/scoped_handle.cc
+++ b/base/win/scoped_handle.cc
@@ -11,6 +11,21 @@
 
 using base::win::internal::ScopedHandleVerifier;
 
+std::ostream& operator<<(std::ostream& os, HandleOperation operation) {
+  switch (operation) {
+    case HandleOperation::kHandleAlreadyTracked:
+      return os << "Handle Already Tracked";
+    case HandleOperation::kCloseHandleNotTracked:
+      return os << "Closing an untracked handle";
+    case HandleOperation::kCloseHandleNotOwner:
+      return os << "Closing a handle owned by something else";
+    case HandleOperation::kCloseHandleHook:
+      return os << "CloseHandleHook validation failure";
+    case HandleOperation::kDuplicateHandleHook:
+      return os << "DuplicateHandleHook validation failure";
+  }
+}
+
 // Static.
 bool HandleTraits::CloseHandle(HANDLE handle) {
   return ScopedHandleVerifier::Get()->CloseHandle(handle);
@@ -36,8 +51,8 @@
   return ScopedHandleVerifier::Get()->Disable();
 }
 
-void OnHandleBeingClosed(HANDLE handle) {
-  return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle);
+void OnHandleBeingClosed(HANDLE handle, HandleOperation operation) {
+  return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle, operation);
 }
 
 }  // namespace win
diff --git a/base/win/scoped_handle.h b/base/win/scoped_handle.h
index bfa8188..3a7d47e 100644
--- a/base/win/scoped_handle.h
+++ b/base/win/scoped_handle.h
@@ -7,6 +7,8 @@
 
 #include "base/win/windows_types.h"
 
+#include <ostream>
+
 #include "base/base_export.h"
 #include "base/check_op.h"
 #include "base/dcheck_is_on.h"
@@ -26,6 +28,16 @@
 namespace base {
 namespace win {
 
+enum class HandleOperation {
+  kHandleAlreadyTracked,
+  kCloseHandleNotTracked,
+  kCloseHandleNotOwner,
+  kCloseHandleHook,
+  kDuplicateHandleHook
+};
+
+std::ostream& operator<<(std::ostream& os, HandleOperation operation);
+
 // Generic wrapper for raw handles that takes care of closing handles
 // automatically. The class interface follows the style of
 // the ScopedFILE class with two additions:
@@ -113,8 +125,9 @@
   }
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, HandleVerifierWrongOwner);
-  FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, HandleVerifierUntrackedHandle);
+  FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest, HandleVerifierWrongOwner);
+  FRIEND_TEST_ALL_PREFIXES(ScopedHandleDeathTest,
+                           HandleVerifierUntrackedHandle);
   Handle handle_;
 };
 
@@ -183,7 +196,7 @@
     GenericScopedHandle<HandleTraits, DummyVerifierTraits>;
 using CheckedScopedHandle = GenericScopedHandle<HandleTraits, VerifierTraits>;
 
-#if DCHECK_IS_ON() && !defined(ARCH_CPU_64_BITS)
+#if DCHECK_IS_ON()
 using ScopedHandle = CheckedScopedHandle;
 #else
 using ScopedHandle = UncheckedScopedHandle;
@@ -198,7 +211,8 @@
 // verification of improper handle closing is desired. If |handle| is being
 // tracked by the handle verifier and ScopedHandle is not the one closing it,
 // a CHECK is generated.
-BASE_EXPORT void OnHandleBeingClosed(HANDLE handle);
+BASE_EXPORT void OnHandleBeingClosed(HANDLE handle, HandleOperation operation);
+
 }  // namespace win
 }  // namespace base
 
diff --git a/base/win/scoped_handle_unittest.cc b/base/win/scoped_handle_unittest.cc
index 0df21ce..228f2eb6 100644
--- a/base/win/scoped_handle_unittest.cc
+++ b/base/win/scoped_handle_unittest.cc
@@ -8,11 +8,13 @@
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/files/file_path.h"
 #include "base/scoped_native_library.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
 #include "base/win/scoped_handle.h"
+#include "build/build_config.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
@@ -24,9 +26,31 @@
 extern "C" bool __declspec(dllexport) RunTest();
 }  // namespace testing
 
-TEST(ScopedHandleTest, ScopedHandle) {
+class ScopedHandleTest : public ::testing::Test,
+                         public ::testing::WithParamInterface<bool> {
+ public:
+  ScopedHandleTest(const ScopedHandleTest&) = delete;
+  ScopedHandleTest& operator=(const ScopedHandleTest&) = delete;
+
+ protected:
+  ScopedHandleTest() {
+    if (HooksEnabled()) {
+#if defined(ARCH_CPU_32_BITS)
+      // EAT patch is only supported on 32-bit.
+      base::debug::HandleHooks::AddEATPatch();
+#endif
+      base::debug::HandleHooks::PatchLoadedModules();
+    }
+  }
+
+  static bool HooksEnabled() { return GetParam(); }
+};
+
+using ScopedHandleDeathTest = ScopedHandleTest;
+
+TEST_P(ScopedHandleTest, ScopedHandle) {
   // Any illegal error code will do. We just need to test that it is preserved
-  // by ScopedHandle to avoid bug 528394.
+  // by ScopedHandle to avoid https://crbug.com/528394.
   const DWORD magic_error = 0x12345678;
 
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
@@ -49,7 +73,7 @@
   EXPECT_EQ(magic_error, ::GetLastError());
 }
 
-TEST(ScopedHandleTest, HandleVerifierTrackedHasBeenClosed) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierTrackedHasBeenClosed) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
   using NtCloseFunc = decltype(&::NtClose);
@@ -64,10 +88,35 @@
         // Destructing a ScopedHandle with an illegally closed handle should
         // fail.
       },
-      "");
+      "CloseHandle failed");
 }
 
-TEST(ScopedHandleTest, HandleVerifierDoubleTracking) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierCloseTrackedHandle) {
+  // This test is only valid if hooks are enabled.
+  if (!HooksEnabled())
+    return;
+  ASSERT_DEATH(
+      {
+        HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
+        ASSERT_NE(HANDLE(nullptr), handle);
+
+        // Start tracking the handle so that closes outside of the checker are
+        // caught.
+        base::win::CheckedScopedHandle handle_holder(handle);
+
+        // Closing a tracked handle using ::CloseHandle should crash due to hook
+        // noticing the illegal close.
+        ::CloseHandle(handle);
+      },
+      // This test must match the CloseHandleHook causing this failure, because
+      // if the hook doesn't crash and instead the handle is double closed by
+      // the `handle_holder` going out of scope, then there is still a crash,
+      // but a different crash and one we are not explicitly testing here. This
+      // other crash is tested in HandleVerifierTrackedHasBeenClosed above.
+      "CloseHandleHook validation failure");
+}
+
+TEST_P(ScopedHandleDeathTest, HandleVerifierDoubleTracking) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -76,7 +125,7 @@
   ASSERT_DEATH({ base::win::CheckedScopedHandle handle_holder2(handle); }, "");
 }
 
-TEST(ScopedHandleTest, HandleVerifierWrongOwner) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierWrongOwner) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -86,12 +135,12 @@
         base::win::CheckedScopedHandle handle_holder2;
         handle_holder2.handle_ = handle;
       },
-      "");
+      "Closing a handle owned by something else");
   ASSERT_TRUE(handle_holder.is_valid());
   handle_holder.Close();
 }
 
-TEST(ScopedHandleTest, HandleVerifierUntrackedHandle) {
+TEST_P(ScopedHandleDeathTest, HandleVerifierUntrackedHandle) {
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
 
@@ -100,7 +149,7 @@
         base::win::CheckedScopedHandle handle_holder;
         handle_holder.handle_ = handle;
       },
-      "");
+      "Closing an untracked handle");
 
   ASSERT_TRUE(::CloseHandle(handle));
 }
@@ -113,7 +162,7 @@
 #define MAYBE_MultiProcess MultiProcess
 #endif
 
-TEST(ScopedHandleTest, MAYBE_MultiProcess) {
+TEST_P(ScopedHandleTest, MAYBE_MultiProcess) {
   // Initializing ICU in the child process causes a scoped handle to be created
   // before the test gets a chance to test the race condition, so disable ICU
   // for the child process here.
@@ -145,5 +194,18 @@
   return 0;
 }
 
+INSTANTIATE_TEST_SUITE_P(HooksEnabled,
+                         ScopedHandleTest,
+                         ::testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(HooksDisabled,
+                         ScopedHandleTest,
+                         ::testing::Values(false));
+INSTANTIATE_TEST_SUITE_P(HooksEnabled,
+                         ScopedHandleDeathTest,
+                         ::testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(HooksDisabled,
+                         ScopedHandleDeathTest,
+                         ::testing::Values(false));
+
 }  // namespace win
 }  // namespace base
diff --git a/base/win/scoped_handle_verifier.cc b/base/win/scoped_handle_verifier.cc
index e64bd2c..e7ab4b9 100644
--- a/base/win/scoped_handle_verifier.cc
+++ b/base/win/scoped_handle_verifier.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/base_tracing.h"
 #include "base/win/base_win_buildflags.h"
 #include "base/win/current_module.h"
+#include "base/win/scoped_handle.h"
 
 extern "C" {
 __declspec(dllexport) void* GetHandleVerifier();
@@ -40,21 +41,25 @@
 using NativeLock = base::internal::LockImpl;
 
 NOINLINE void ReportErrorOnScopedHandleOperation(
-    const base::debug::StackTrace& creation_stack) {
+    const base::debug::StackTrace& creation_stack,
+    HandleOperation operation) {
   auto creation_stack_copy = creation_stack;
   base::debug::Alias(&creation_stack_copy);
-  CHECK(false);
+  base::debug::Alias(&operation);
+  CHECK(false) << operation;
   __builtin_unreachable();
 }
 
 NOINLINE void ReportErrorOnScopedHandleOperation(
     const base::debug::StackTrace& creation_stack,
-    const ScopedHandleVerifierInfo& other) {
+    const ScopedHandleVerifierInfo& other,
+    HandleOperation operation) {
   auto other_stack_copy = *other.stack;
   base::debug::Alias(&other_stack_copy);
   auto creation_stack_copy = creation_stack;
   base::debug::Alias(&creation_stack_copy);
-  CHECK(false);
+  base::debug::Alias(&operation);
+  CHECK(false) << operation;
   __builtin_unreachable();
 }
 
@@ -107,7 +112,7 @@
 
 bool CloseHandleWrapper(HANDLE handle) {
   if (!::CloseHandle(handle))
-    CHECK(false);  // CloseHandle failed.
+    CHECK(false) << "CloseHandle failed";
   return true;
 }
 
@@ -202,9 +207,10 @@
   enabled_ = false;
 }
 
-void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle) {
+void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle,
+                                               HandleOperation operation) {
   if (enabled_)
-    OnHandleBeingClosedImpl(handle);
+    OnHandleBeingClosedImpl(handle, operation);
 }
 
 HMODULE ScopedHandleVerifier::GetModule() const {
@@ -227,7 +233,8 @@
                                        thread_id});
   if (!result.second) {
     // Attempt to start tracking already tracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_, result.first->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, result.first->second,
+                                       HandleOperation::kHandleAlreadyTracked);
   }
 }
 
@@ -239,18 +246,22 @@
   HandleMap::iterator i = map_.find(handle);
   if (i == map_.end()) {
     // Attempting to close an untracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_);
+    ReportErrorOnScopedHandleOperation(creation_stack_,
+                                       HandleOperation::kCloseHandleNotTracked);
   }
 
   if (i->second.owner != owner) {
     // Attempting to close a handle not owned by opener.
-    ReportErrorOnScopedHandleOperation(creation_stack_, i->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, i->second,
+                                       HandleOperation::kCloseHandleNotOwner);
   }
 
   map_.erase(i);
 }
 
-NOINLINE void ScopedHandleVerifier::OnHandleBeingClosedImpl(HANDLE handle) {
+NOINLINE void ScopedHandleVerifier::OnHandleBeingClosedImpl(
+    HANDLE handle,
+    HandleOperation operation) {
   if (closing_.Get())
     return;
 
@@ -258,7 +269,7 @@
   HandleMap::iterator i = map_.find(handle);
   if (i != map_.end()) {
     // CloseHandle called on tracked handle.
-    ReportErrorOnScopedHandleOperation(creation_stack_, i->second);
+    ReportErrorOnScopedHandleOperation(creation_stack_, i->second, operation);
   }
 }
 
diff --git a/base/win/scoped_handle_verifier.h b/base/win/scoped_handle_verifier.h
index 84bb547d..7085e99 100644
--- a/base/win/scoped_handle_verifier.h
+++ b/base/win/scoped_handle_verifier.h
@@ -18,6 +18,7 @@
 
 namespace base {
 namespace win {
+enum class HandleOperation;
 namespace internal {
 
 struct HandleHash {
@@ -76,7 +77,7 @@
   virtual void StopTracking(HANDLE handle, const void* owner, const void* pc1,
                             const void* pc2);
   virtual void Disable();
-  virtual void OnHandleBeingClosed(HANDLE handle);
+  virtual void OnHandleBeingClosed(HANDLE handle, HandleOperation operation);
   virtual HMODULE GetModule() const;
 
  private:
@@ -86,7 +87,7 @@
                          const void* pc2);
   void StopTrackingImpl(HANDLE handle, const void* owner, const void* pc1,
                         const void* pc2);
-  void OnHandleBeingClosedImpl(HANDLE handle);
+  void OnHandleBeingClosedImpl(HANDLE handle, HandleOperation operation);
 
   static base::internal::LockImpl* GetLock();
   static void InstallVerifier();
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 6fd132f..ad16a8a 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -464,6 +464,10 @@
         'com.google.common.flogger.backend.google.GooglePlatform',
         'com.google.common.flogger.backend.system.DefaultPlatform',
 
+        # trichrome_webview_google_bundle contains this missing reference.
+        # TODO(crbug.com/1142530): Fix this missing reference properly.
+        'org.chromium.build.NativeLibraries',
+
         # TODO(agrieve): Exclude these only when use_jacoco_coverage=true.
         'java.lang.instrument.ClassFileTransformer',
         'java.lang.instrument.IllegalClassFormatException',
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 6c119948..1a9b3736 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2133,7 +2133,6 @@
   #     uncompressed in the APK. Must be unset or true if load_library_from_apk
   #     is set to true.
   #   uncompress_dex: Store final .dex files uncompressed in the apk.
-  #   omit_dex: If true, do not build or include classes.dex.
   #   strip_resource_names: True if resource names should be stripped from the
   #     resources.arsc file in the apk or module.
   #   strip_unused_resources: True if unused resources should be stripped from
@@ -2216,7 +2215,6 @@
           defined(invoker.is_base_module) && invoker.is_base_module
     }
 
-    _omit_dex = defined(invoker.omit_dex) && invoker.omit_dex
     _enable_multidex =
         !defined(invoker.enable_multidex) || invoker.enable_multidex
 
@@ -2360,13 +2358,12 @@
     _rebased_build_config = rebase_path(_build_config, root_build_dir)
     assert(_rebased_build_config != "")  # Mark as used.
 
-    _generate_buildconfig_java = !defined(invoker.apk_under_test) && !_omit_dex
+    _generate_buildconfig_java = !defined(invoker.apk_under_test)
     if (defined(invoker.generate_buildconfig_java)) {
       _generate_buildconfig_java = invoker.generate_buildconfig_java
     }
 
-    _generate_productconfig_java =
-        defined(invoker.product_config_java_packages) && !_omit_dex
+    _generate_productconfig_java = defined(invoker.product_config_java_packages)
 
     # JNI generation usually goes hand-in-hand with buildconfig generation.
     _generate_final_jni = _generate_buildconfig_java
@@ -2430,7 +2427,7 @@
       _incremental_apk_path = "${_final_apk_path_no_ext}_incremental.apk"
     }
 
-    if (!_incremental_apk && !_omit_dex) {
+    if (!_incremental_apk) {
       # Bundle modules don't build the dex here, but need to write this path
       # to their .build_config.json file.
       if (_proguard_enabled) {
@@ -2687,9 +2684,8 @@
     } else {
       _generate_native_libraries_java =
           (!_is_bundle_module || _is_base_module) &&
-          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != [] ||
-           defined(invoker.static_library_provider)) &&
-          !_uses_static_library_synchronized_proguard && !_omit_dex
+          (_native_libs_deps != [] || _secondary_abi_native_libs_deps != []) &&
+          !_uses_static_library_synchronized_proguard
     }
     if (_generate_native_libraries_java) {
       write_native_libraries_java("${_template_name}__native_libraries") {
@@ -2698,17 +2694,7 @@
         # Do not add a dep on the generated_file target in order to avoid having
         # to build the native libraries before this target. The dependency is
         # instead captured via a depfile.
-        if (_uses_static_library) {
-          _prefix = get_label_info(invoker.static_library_provider,
-                                   "target_gen_dir") + "/" +
-                    get_label_info(invoker.static_library_provider, "name")
-          if (defined(invoker.static_library_provider_use_secondary_abi) &&
-              invoker.static_library_provider_use_secondary_abi) {
-            native_libraries_list_file = "${_prefix}.secondary_abi_native_libs"
-          } else {
-            native_libraries_list_file = "${_prefix}.native_libs"
-          }
-        } else if (_native_libs_deps != []) {
+        if (_native_libs_deps != []) {
           native_libraries_list_file = _shared_library_list_file
         } else {
           native_libraries_list_file = _secondary_abi_shared_library_list_file
@@ -2804,15 +2790,14 @@
                  ])
     }
 
+    _java_target = "${_template_name}__java"
+
     if (_is_bundle_module) {
       _add_view_trace_events =
           defined(invoker.add_view_trace_events) &&
           invoker.add_view_trace_events && enable_trace_event_bytecode_rewriting
     }
 
-    # We cannot skip this target when omit_dex = true because it writes the
-    # build_config.json.
-    _java_target = "${_template_name}__java"
     java_library_impl(_java_target) {
       forward_variables_from(invoker,
                              [
@@ -2944,7 +2929,7 @@
 
     if (_uses_static_library_synchronized_proguard) {
       _final_dex_target_dep = "${invoker.static_library_provider}__dexsplitter"
-    } else if ((_is_bundle_module && _proguard_enabled) || _omit_dex) {
+    } else if (_is_bundle_module && _proguard_enabled) {
       _final_deps += [ ":$_java_target" ]
     } else if (_incremental_apk) {
       if (defined(invoker.enable_proguard_checks)) {
@@ -3220,7 +3205,7 @@
         deps = _deps + [ ":$_build_config_target" ]
 
         if ((!_proguard_enabled || _incremental_apk) &&
-            enable_jdk_library_desugaring && !_omit_dex) {
+            enable_jdk_library_desugaring) {
           _all_jdk_libs = "//build/android:all_jdk_libs"
           deps += [ _all_jdk_libs ]
           jdk_libs_dex = get_label_info(_all_jdk_libs, "target_out_dir") +
@@ -3500,7 +3485,6 @@
                                "expected_libs_and_assets_base",
                                "generate_buildconfig_java",
                                "generate_final_jni",
-                               "generate_native_libraries_java",
                                "include_size_info",
                                "input_jars_paths",
                                "use_modern_linker",
@@ -3525,7 +3509,6 @@
                                "native_lib_placeholders",
                                "never_incremental",
                                "no_xml_namespaces",
-                               "omit_dex",
                                "png_to_webp",
                                "post_process_package_resources_script",
                                "processor_args_javac",
@@ -3550,7 +3533,6 @@
                                "srcjar_deps",
                                "static_library_dependent_targets",
                                "static_library_provider",
-                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "target_sdk_version",
                                "testonly",
@@ -3650,7 +3632,6 @@
                                "load_library_from_apk",
                                "loadable_modules",
                                "product_config_java_packages",
-                               "main_component_library",
                                "manifest_package",
                                "max_sdk_version",
                                "min_sdk_version",
@@ -3679,7 +3660,6 @@
                                "short_resource_paths",
                                "srcjar_deps",
                                "static_library_provider",
-                               "static_library_provider_use_secondary_abi",
                                "static_library_synchronized_proguard",
                                "strip_resource_names",
                                "strip_unused_resources",
diff --git a/build/fuchsia/cipd/BUILD.gn b/build/fuchsia/cipd/BUILD.gn
index f04d48a..839ce6fd 100644
--- a/build/fuchsia/cipd/BUILD.gn
+++ b/build/fuchsia/cipd/BUILD.gn
@@ -14,13 +14,6 @@
 
 visibility = [ ":*" ]
 
-# gn binary location.
-if (host_os == "mac") {
-  _gn_path = "//buildtools/mac/gn"
-} else if (host_os == "linux") {
-  _gn_path = "//buildtools/linux64/gn"
-}
-
 if (is_chrome_branded) {
   package_base_path = "chrome_internal/fuchsia"
 } else {
@@ -92,6 +85,14 @@
     _license_path = "${target_gen_dir}/${target_name}/LICENSE"
     _invoker_dir = get_label_info(":${invoker.target_name}", "dir")
     _license_target = "${_invoker_dir}:${invoker.target_name}${_archive_suffix}"
+
+    # GN is used by the script and is thus an input.
+    if (host_os == "mac") {
+      _gn_path = "//buildtools/mac/gn"
+    } else if (host_os == "linux") {
+      _gn_path = "//buildtools/linux64/gn"
+    }
+
     script = "//tools/licenses.py"
     inputs = [ "$_gn_path" ]
     outputs = [ _license_path ]
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index a225751c..9916bf6 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220519.2.1
+8.20220519.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index 94d4830..1575c7d7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=104
 MINOR=0
-BUILD=5073
+BUILD=5074
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 42e4e794..fa26347 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2916,6 +2916,12 @@
   }
 }
 
+# TODO(agrieve): Remove this once we switch to using bundle targets to
+# generate APK stubs.
+android_resources("trichrome_dummy_resources") {
+  sources = [ "trichrome/res_dummy/values/strings.xml" ]
+}
+
 chrome_public_unit_test_apk_manifest =
     "$root_gen_dir/chrome_public_unit_test_apk_manifest/AndroidManifest.xml"
 chrome_public_test_apk_manifest =
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 87220dc..6fb71709 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -235,19 +235,6 @@
       use_chromium_linker = chromium_linker_supported
     }
 
-    if (_is_trichrome) {
-      static_library_provider_use_secondary_abi = _is_secondary_abi_primary
-
-      # http://crbug.com/1042107.
-      if (is_component_build) {
-        if (android_64bit_target_cpu && _is_64_bit_browser) {
-          main_component_library = "libmonochrome_64.cr.so"
-        } else {
-          main_component_library = "libmonochrome.cr.so"
-        }
-      }
-    }
-
     if (!_is_monochrome && !_is_trichrome) {
       deps += [
         "//chrome/android:chrome_public_v8_assets",
diff --git a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
index cb6d641c..fd2f714 100644
--- a/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_library_apk.AndroidManifest.expected
@@ -10,7 +10,6 @@
   <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
   <application
       android:extractNativeLibs="false"
-      android:hasCode="false"
       android:icon="@drawable/icon_webview"
       android:label="Trichrome Library"
       android:multiArch="true"
diff --git a/chrome/android/java/AndroidManifest_trichrome_library.xml b/chrome/android/java/AndroidManifest_trichrome_library.xml
index 070efc3..97e9ddd 100644
--- a/chrome/android/java/AndroidManifest_trichrome_library.xml
+++ b/chrome/android/java/AndroidManifest_trichrome_library.xml
@@ -18,7 +18,6 @@
 
     <!-- TODO(torne): we should specify an icon, roundIcon, and label from resources. -->
     <application
-        android:hasCode="false"
         android:label="{{ application_label|default('Trichrome Library') }}"
         android:icon="@drawable/icon_webview"
         android:multiArch="true"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index a51749f..c5742ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -179,6 +179,7 @@
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.undo_tab_close_snackbar.UndoBarController;
 import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
@@ -2230,6 +2231,12 @@
             ReadingListUtils.showReadingList(currentTab.isIncognito());
         }
 
+        // At this point we know either the tab will close or the app will minimize.
+        NativePage nativePage = currentTab.getNativePage();
+        if (nativePage != null) {
+            nativePage.notifyHidingWithBack();
+        }
+
         final boolean shouldCloseTab = backShouldCloseTab(currentTab);
 
         // Minimize the app if either:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index ad5813b4..1478e321 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -714,6 +714,14 @@
     }
 
     @Override
+    public void notifyHidingWithBack() {
+        FeedReliabilityLogger feedReliabilityLogger = mFeedSurfaceProvider.getReliabilityLogger();
+        if (feedReliabilityLogger != null) {
+            feedReliabilityLogger.onNavigateBack();
+        }
+    }
+
+    @Override
     public void onVoiceAvailabilityImpacted() {
         mNewTabPageLayout.updateActionButtonVisibility();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 27516a8..d5ef3bb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -57,6 +57,7 @@
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.feed.FeedReliabilityLogger;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -653,6 +654,17 @@
                 "feed_is_ablated");
     }
 
+    @Test
+    @SmallTest
+    @Feature({"NewTabPage", "FeedNewTabPage"})
+    public void testFeedReliabilityLoggingHideWithBack() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            ChromeTabbedActivity activity = (ChromeTabbedActivity) mActivityTestRule.getActivity();
+            activity.handleBackPressed();
+            verify(mFeedReliabilityLogger).onNavigateBack();
+        });
+    }
+
     private void assertThumbnailInvalidAndRecapture() {
         Assert.assertTrue(mNtp.shouldCaptureThumbnail());
         captureThumbnail();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
index 0df232c7..9fcf062d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImplTest.java
@@ -38,6 +38,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.feature_engagement.EventConstants;
@@ -85,6 +86,8 @@
     public Profile.Natives mMockProfileNatives;
     @Mock
     private ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
+    @Mock
+    private NativePage mNativePage;
 
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -98,6 +101,7 @@
         doReturn(false).when(mOverrideHomePageSupplier).get();
         mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
         doReturn(mProfile).when(mMockProfileNatives).fromWebContents(any());
+        doReturn(mNativePage).when(mTab).getNativePage();
         TrackerFactory.setTrackerForTests(mTracker);
         doReturn(new ObservableSupplierImpl<>()).when(mTabModelSelectorSupplier).get();
         mToolbarTabController = new ToolbarTabControllerImpl(mTabSupplier,
@@ -112,6 +116,7 @@
 
         assertFalse(mToolbarTabController.forward());
         assertFalse(mToolbarTabController.back());
+        verify(mNativePage, never()).notifyHidingWithBack();
     }
 
     @Test
@@ -138,6 +143,15 @@
     }
 
     @Test
+    public void back_notifyNativePageHiding() {
+        doReturn(null).when(mBottomControlsCoordinatorSupplier).get();
+        doReturn(true).when(mTab).canGoBack();
+
+        mToolbarTabController.back();
+        verify(mNativePage).notifyHidingWithBack();
+    }
+
+    @Test
     public void stopOrReloadCurrentTab() {
         doReturn(false).when(mTab).isLoading();
         mToolbarTabController.stopOrReloadCurrentTab();
diff --git a/chrome/android/proguard/trichrome.flags b/chrome/android/proguard/trichrome.flags
new file mode 100644
index 0000000..68fb39a
--- /dev/null
+++ b/chrome/android/proguard/trichrome.flags
@@ -0,0 +1,8 @@
+# Copyright 2018 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.
+
+# TODO(agrieve): Once this -keep is removed, add a @CheckDiscard to LibraryLoaderConfig.java.
+# Currently the Trichrome library just contains NativeLibraries, which we keep.
+# https://crbug.com/901465
+-keep class org.chromium.build.NativeLibraries { *; }
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index 1e2eec7..01b7f0b 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -114,7 +114,7 @@
       product_version_resources_dep =
           "//chrome/android:product_version_resources"
     } else {
-      omit_dex = true
+      generate_buildconfig_java = false
     }
 
     # TODO(torne): using icon_resources just to get a temporary icon
@@ -194,6 +194,37 @@
         }
       }
     }
+
+    # http://crbug.com/1042107.
+    if (is_component_build) {
+      if (android_64bit_target_cpu && invoker.is_64_bit_browser) {
+        main_component_library = "libmonochrome_64.cr.so"
+      } else {
+        main_component_library = "libmonochrome.cr.so"
+      }
+    }
+
+    if (!is_java_debug) {
+      proguard_enabled = true
+      proguard_configs = [
+        "//base/android/proguard/chromium_apk.flags",
+        "//base/android/proguard/chromium_code.flags",
+        "//chrome/android/proguard/trichrome.flags",
+      ]
+      if (trichrome_synchronized_proguard) {
+        proguard_configs += [
+          "//chrome/android/proguard/static_library_dex_reference_workarounds.flags",
+          "//base/android/proguard/enable_obfuscation.flags",
+        ]
+      } else {
+        # Disabling all obfuscation for the Trichrome library as a temporary
+        # workaround for crbug.com/1012842. There were naming conflicts between
+        # Library and Chrome, since each Proguard run doesn't know about the
+        # other, and thus handed out the first names (a, b, c) to both.
+        proguard_enable_obfuscation = false
+      }
+    }
+    deps += [ "//chrome/android:trichrome_dummy_resources" ]
   }
 }
 
diff --git a/chrome/android/trichrome/res_dummy/values/strings.xml b/chrome/android/trichrome/res_dummy/values/strings.xml
new file mode 100644
index 0000000..a0d71c24
--- /dev/null
+++ b/chrome/android/trichrome/res_dummy/values/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 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. -->
+
+<!-- DO NOT ADD MORE RESOURCES HERE -->
+<resources>
+    <string name="dummy"></string>
+</resources>
\ No newline at end of file
diff --git a/chrome/android/trichrome/static_library_shared_java_code.md b/chrome/android/trichrome/static_library_shared_java_code.md
new file mode 100644
index 0000000..a288902
--- /dev/null
+++ b/chrome/android/trichrome/static_library_shared_java_code.md
@@ -0,0 +1,100 @@
+# Static Library Java code
+
+[TOC]
+
+## Overview
+
+This document describes how static library targets can be used to share common
+Java code between multiple APKs. More detail can be found at
+[go/proguarding-trichrome](goto.google.com/proguarding-trichrome).
+
+## TrichromeLibrary
+
+Currently (Jan 2020) trichrome library is the only target to make use of static
+shared library APKs and is used to share common code used by both Chrome and
+Webview.
+
+## Status
+
+Java code sharing is mostly implemented at this point but there is one remaining
+blocker related to how
+[native method resolution works in Webview](crbug.com/1025009).
+
+## How it works
+
+### Build variables
+
+For `android_apk_or_module` base templates:
+
+`static_library_provider`: Specifies that this target depends on a static shared
+library APK. When synchronized proguard is turned on, the
+`static_library_provider` becomes the target that provides the final dex file.
+
+`static_library_dependent_targets`: If set, generates final dex files for
+itself and for all targets in the `static_library_dependent_targets` list.
+
+`static_library_synchronized_proguard`: Turns on synchronized proguard for
+targets that also set `static_library_provider`.
+
+### .build_config
+
+`write_build_config.py` is responsible for figuring out where code and related
+artifacts for the `static_library_provider` and
+`static_library_dependent_targets` belongs. The main difference from regular
+`.build_configs` is the mapping recording which input jars belong to each final
+dex file. Ex:
+
+```
+"deps_info": {
+  ...
+  "static_library_dependent_classpath_configs": {
+      "gen/android_webview/trichrome_webview_apk.build_config.json": [
+        "obj/android_webview/trichrome_webview_apk/trichrome_webview_apk.jar",
+        ...
+      ],
+      "gen/chrome/android/trichrome_chrome_bundle.build_config.json": [
+        "lib.java/chrome/android/app_hooks_java.jar",
+        ...
+      "gen/chrome/android/trichrome_library_apk.build_config.json": [
+        "lib.java/base/base_java.jar",
+        ...
+      ]
+      ...
+  }
+}
+```
+
+### Synchronized ProGuard
+
+TrichromeChromeBundle (base module) and TrichromeWebview do not have a final
+`dex` or `proguard` step. Instead the library APK creates a "fat" dex from the
+`.build_config.json:deps_info:java_runtime_classpath`.
+
+Then, the mapping of `.build_config.json` -> owned input jars stored in the
+`.build_config.json` is used by `dexsplitter` to generate final .dex files for
+TrichromeLibrary, TrichromeChrome, and TrichromeWebview.
+
+### Resources
+
+For Java code to be shared between Chrome and Webview in [Trichrome][trichrome],
+we ensure that Chrome and Webview use the same resource IDs. This requires a
+few adjustments to how resources are created.
+
+1. Webview's resources are compiled first without any changes.
+2. Chrome's resources are compiled second, but use the same resource IDs as
+   Webview when possible.
+3. When synchronized proguarding is turned on, the `R.java` files generated in
+   the previous step are discarded. The shared static library APK target
+   (trichrome library) takes the output `R.txt` files from the previous steps
+   and includes those resources in its own `R.java` generation.
+
+[trichrome]: /chrome/android/trichrome/static_library_shared_java_code.md
+
+### Usage
+
+* Building trichrome_chrome_bundle or trichrome_webview_apk (and various arch
+  variants) will ensure the correct library target is also built.
+* Using the generated wrapper script from the main APK is sufficient (no need
+  to explicitly install the library).
+  * `bin/trichrome_chrome_bundle run` will ensure TrichromeChromeBundle and
+    TrichromeLibrary are installed before launching Chrome.
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 6ae82940..de985ff 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -85,7 +85,7 @@
 
 #include <algorithm>
 
-#include "base/debug/close_handle_hook_win.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/files/important_file_writer_cleaner.h"
 #include "base/threading/platform_thread_win.h"
 #include "base/win/atl.h"
@@ -808,8 +808,8 @@
     *exit_code = 0;
     return true;  // Got a --version switch; exit with a success error code.
   }
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
+  // TODO(crbug.com/1052397): Revisit the macro expression once build flag
+  // switch of lacros-chrome is complete.
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // This will directly exit if the user asked for help.
   HandleHelpSwitches(command_line);
@@ -823,11 +823,25 @@
     return true;
   }
 
-// HandleVerifier detects and reports incorrect handle manipulations. It tracks
-// handle operations on builds that support DCHECK only.
-// TODO(crbug/1104358): Support 64-bit handle hooks.
-#if DCHECK_IS_ON() && !defined(ARCH_CPU_64_BITS)
-  base::debug::InstallHandleHooks();
+  // HandleVerifier detects and reports incorrect handle manipulations. It
+  // tracks handle operations on builds that support DCHECK only.
+#if DCHECK_IS_ON()
+  // This portion of the hook setup is just for child processes. Browser part is
+  // in ChromeBrowserMainPartsWin::PostProfileInit.
+  if (!is_browser) {
+    // Performing EAT interception first is safer in the presence of other
+    // threads attempting to call CloseHandle.
+#if defined(ARCH_CPU_32_BITS)
+    // Patching EAT of kernel32.dll is only supported on 32-bit because RVA can
+    // only hold 32-bit values.
+    base::debug::HandleHooks::AddEATPatch();
+#endif
+    // Patch once. Cannot monitor for further modules in a child process as
+    // monitoring needs ModuleWatcher, but likely no more should really load in
+    // a child process from this point on. If we miss any then we will lose some
+    // detection but still generate no false positive crashes.
+    base::debug::HandleHooks::PatchLoadedModules();
+  }
 #else
   base::win::DisableHandleVerifier();
 #endif
@@ -1076,6 +1090,18 @@
           lacros_resources_dir.Append(FILE_PATH_LITERAL("resources.pak")),
           user_data_dir.Append(crosapi::kSharedResourcesPackName),
           ui::kScaleFactorNone);
+      ui::DataPackWithResourceSharing::MaybeGenerateFallbackAndMapping(
+          ash_resources_dir.Append(FILE_PATH_LITERAL("chrome_100_percent.pak")),
+          lacros_resources_dir.Append(
+              FILE_PATH_LITERAL("chrome_100_percent.pak")),
+          user_data_dir.Append(crosapi::kSharedChrome100PercentPackName),
+          ui::k100Percent);
+      ui::DataPackWithResourceSharing::MaybeGenerateFallbackAndMapping(
+          ash_resources_dir.Append(FILE_PATH_LITERAL("chrome_200_percent.pak")),
+          lacros_resources_dir.Append(
+              FILE_PATH_LITERAL("chrome_200_percent.pak")),
+          user_data_dir.Append(crosapi::kSharedChrome200PercentPackName),
+          ui::k200Percent);
     }
   }
 #endif
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 55a0a7a..5511012 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -13064,6 +13064,9 @@
       <message name="IDS_INPUT_OVERLAY_EDUCATIONAL_BETA" desc="Label marking the feature as 'Beta', this has to be removed after the feature is fully approved.">
         Beta
       </message>
+      <message name="IDS_INPUT_OVERLAY_SETTINGS_NUDGE" desc="Label displayed next to the Settings UI's entry point so the user is aware.">
+        Click here to customize controls
+      </message>
     </if>
 
     <!-- Privacy Sandbox Dialog strings -->
diff --git a/chrome/app/generated_resources_grd/IDS_INPUT_OVERLAY_SETTINGS_NUDGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_INPUT_OVERLAY_SETTINGS_NUDGE.png.sha1
new file mode 100644
index 0000000..cc0b027
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_INPUT_OVERLAY_SETTINGS_NUDGE.png.sha1
@@ -0,0 +1 @@
+b60e34179fcc2ab32e3727ec98536f280e51be5b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 80e85b8..de7f049 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1917,6 +1917,9 @@
   <message name="IDS_SETTINGS_CROSTINI_SUBTEXT" desc="Description for the section for enabling and managing Crostini.">
     Run Linux tools, editors, and IDEs on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
+  <message name="IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED" desc="Description for the Developers section when crostini is not supported due to hardware limitation or account restrictions.">
+    Linux is not supported on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. <ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
   <message name="IDS_SETTINGS_CROSTINI_REMOVE" desc="Label for the row to open a dialog confirming removal of Crostini.">
     Remove Linux development environment
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED.png.sha1
new file mode 100644
index 0000000..d39ae44c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED.png.sha1
@@ -0,0 +1 @@
+329a2b7307dc1face48c6c475238225e74b77908
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 7c3c24e..f1d2c439 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -239,6 +239,7 @@
       "sharesheet_share_with_others.icon",
       "sharesheet_text.icon",
       "shutdown_guest_os.icon",
+      "tip.icon",
     ]
   }
 
diff --git a/chrome/app/vector_icons/tip.icon b/chrome/app/vector_icons/tip.icon
new file mode 100644
index 0000000..118ba3e4
--- /dev/null
+++ b/chrome/app/vector_icons/tip.icon
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 10.42f, 7.08f,
+LINE_TO, 8.33f, 2.5f,
+LINE_TO, 6.25f, 7.08f,
+LINE_TO, 1.67f, 9.17f,
+LINE_TO, 6.25f, 11.25f,
+LINE_TO, 8.33f, 15.83f,
+LINE_TO, 10.42f, 11.25f,
+LINE_TO, 15, 9.17f,
+LINE_TO, 10.42f, 7.08f,
+CLOSE,
+MOVE_TO, 15, 10.83f,
+LINE_TO, 13.96f, 13.13f,
+LINE_TO, 11.67f, 14.17f,
+LINE_TO, 13.96f, 15.21f,
+LINE_TO, 15, 17.5f,
+LINE_TO, 16.04f, 15.21f,
+LINE_TO, 18.33f, 14.17f,
+LINE_TO, 16.04f, 13.13f,
+LINE_TO, 15, 10.83f,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d091dfe..ff7efb1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1537,8 +1537,6 @@
     "segmentation_platform/default_model/feed_user_segment.h",
     "segmentation_platform/default_model/low_user_engagement_model.cc",
     "segmentation_platform/default_model/low_user_engagement_model.h",
-    "segmentation_platform/default_model/metadata_writer.cc",
-    "segmentation_platform/default_model/metadata_writer.h",
     "segmentation_platform/default_model/query_tiles_model.cc",
     "segmentation_platform/default_model/query_tiles_model.h",
     "segmentation_platform/model_provider_factory_impl.cc",
@@ -5811,6 +5809,8 @@
       "mac/auth_session_request.mm",
       "mac/bluetooth_utility.h",
       "mac/bluetooth_utility.mm",
+      "mac/chrome_browser_main_extra_parts_mac.h",
+      "mac/chrome_browser_main_extra_parts_mac.mm",
       "mac/dock.h",
       "mac/dock.mm",
       "mac/exception_processor.h",
@@ -7702,6 +7702,7 @@
     "//chrome/browser/media:mojo_bindings_js",
     "//chrome/browser/resources/accessibility:build_ts",
     "//chrome/browser/resources/local_state:build",
+    "//chrome/browser/resources/memory_internals:build_ts",
     "//chrome/browser/resources/predictors:build_ts",
     "//chrome/browser/ui/webui/internals/user_education:mojo_bindings_js",
     "//components/site_engagement/core/mojom:mojo_bindings_webui_js",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d6f5fa6..885dfff 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -76,7 +76,6 @@
 #include "components/autofill_assistant/browser/features.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/browsing_data/core/features.h"
-#include "components/certificate_transparency/ct_features.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/flag_descriptions.h"
 #include "components/component_updater/component_updater_command_line_config_policy.h"
@@ -1498,6 +1497,26 @@
     {" alternate hover card format", kTabHoverCardImagesAlternateFormat,
      std::size(kTabHoverCardImagesAlternateFormat), nullptr}};
 
+const FeatureEntry::FeatureParam kSharedHighlightingMaxContextWords5[] = {
+    {shared_highlighting::kSharedHighlightingRefinedMaxContextWordsName, "5"}};
+const FeatureEntry::FeatureParam kSharedHighlightingMaxContextWords10[] = {
+    {shared_highlighting::kSharedHighlightingRefinedMaxContextWordsName, "10"}};
+const FeatureEntry::FeatureParam kSharedHighlightingMaxContextWords15[] = {
+    {shared_highlighting::kSharedHighlightingRefinedMaxContextWordsName, "15"}};
+const FeatureEntry::FeatureParam kSharedHighlightingMaxContextWords20[] = {
+    {shared_highlighting::kSharedHighlightingRefinedMaxContextWordsName, "20"}};
+
+const FeatureEntry::FeatureVariation
+    kSharedHighlightingMaxContextWordsVariations[] = {
+        {" - maxContextWords: 5", kSharedHighlightingMaxContextWords5,
+         std::size(kSharedHighlightingMaxContextWords5), nullptr},
+        {" - maxContextWords: 10", kSharedHighlightingMaxContextWords10,
+         std::size(kSharedHighlightingMaxContextWords10), nullptr},
+        {" - maxContextWords: 15", kSharedHighlightingMaxContextWords15,
+         std::size(kSharedHighlightingMaxContextWords15), nullptr},
+        {" - maxContextWords: 20", kSharedHighlightingMaxContextWords20,
+         std::size(kSharedHighlightingMaxContextWords20), nullptr}};
+
 #if !BUILDFLAG(IS_ANDROID)
 
 const FeatureEntry::FeatureParam kNtpChromeCartModuleFakeData[] = {
@@ -6943,6 +6962,14 @@
      flag_descriptions::kSharedHighlightingRefinedBlocklistDescription, kOsAll,
      FEATURE_VALUE_TYPE(
          shared_highlighting::kSharedHighlightingRefinedBlocklist)},
+    {"shared-highlighting-refined-maxcontextwords",
+     flag_descriptions::kSharedHighlightingRefinedMaxContextWordsName,
+     flag_descriptions::kSharedHighlightingRefinedMaxContextWordsDescription,
+     kOsAll,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(
+         shared_highlighting::kSharedHighlightingRefinedMaxContextWords,
+         kSharedHighlightingMaxContextWordsVariations,
+         "SharedHighlightingRefinedMaxContextWords")},
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"shimless-rma-flow", flag_descriptions::kShimlessRMAFlowName,
@@ -8330,19 +8357,6 @@
          autofill::features::
              kAutofillEnableVirtualCardManagementInDesktopSettingsPage)},
 
-    {"certificate-transparency-2022-policy",
-     flag_descriptions::kCertificateTransparency2022PolicyName,
-     flag_descriptions::kCertificateTransparency2022PolicyDescription, kOsAll,
-     FEATURE_VALUE_TYPE(certificate_transparency::features::
-                            kCertificateTransparency2022Policy)},
-
-    {"certificate-transparency-2022-policy-all-certs",
-     flag_descriptions::kCertificateTransparency2022PolicyAllCertsName,
-     flag_descriptions::kCertificateTransparency2022PolicyAllCertsDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(certificate_transparency::features::
-                            kCertificateTransparency2022PolicyAllCerts)},
-
     {"leak-detection-unauthenticated",
      flag_descriptions::kLeakDetectionUnauthenticated,
      flag_descriptions::kLeakDetectionUnauthenticatedDescription, kOsAll,
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 5a7bc60..2035c386 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -6,9 +6,14 @@
 
 #include <memory>
 
+#include "ash/components/arc/compat_mode/style/arc_color_provider.h"
 #include "ash/frame/non_client_frame_view_ash.h"
+#include "ash/public/cpp/style/color_provider.h"
 #include "ash/shell.h"
+#include "ash/style/ash_color_provider.h"
+#include "ash/style/pill_button.h"
 #include "base/bind.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/edit_mode_exit_view.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/educational_view.h"
 #include "chrome/browser/ash/arc/input_overlay/ui/error_view.h"
@@ -35,6 +40,8 @@
 constexpr int kEditModeExitHeight = 184;
 constexpr SkColor kMenuEntryBgColor = SkColorSetA(SK_ColorWHITE, 0x99);
 constexpr int kCornerRadius = 8;
+constexpr int kNudgeVerticalAlign = 8;
+constexpr int kNudgeHeight = 40;
 
 }  // namespace
 
@@ -95,6 +102,51 @@
   }
 }
 
+void DisplayOverlayController::AddNudgeView(views::Widget* overlay_widget) {
+  if (nudge_view_)
+    return;
+  DCHECK(overlay_widget);
+  auto nudge_view = std::make_unique<ash::PillButton>(
+      base::BindRepeating(&DisplayOverlayController::OnNudgeDismissed,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_INPUT_OVERLAY_SETTINGS_NUDGE),
+      ash::PillButton::Type::kIcon, &kTipIcon);
+  nudge_view->SetSize(
+      gfx::Size(nudge_view->GetPreferredSize().width(), kNudgeHeight));
+  nudge_view->SetButtonTextColor(GetContentLayerColor(
+      ash::AshColorProvider::ContentLayerType::kTextColorPrimary));
+  auto* color_provider = ash::AshColorProvider::Get();
+  DCHECK(color_provider);
+  // TODO(djacobo|cuicuiruan): Retrieve colors via a single provider.
+  nudge_view->SetBackgroundColor(color_provider->GetContentLayerColor(
+      ash::AshColorProvider::ContentLayerType::kButtonLabelColorBlue));
+  nudge_view->SetIconColor(GetContentLayerColor(
+      ash::AshColorProvider::ContentLayerType::kIconColorPrimary));
+  nudge_view->SetPosition(CalculateNudgePosition(nudge_view->width()));
+
+  auto* parent = overlay_widget->GetContentsView();
+  DCHECK(parent);
+  nudge_view_ = parent->AddChildView(std::move(nudge_view));
+}
+
+void DisplayOverlayController::RemoveNudgeView() {
+  if (!nudge_view_)
+    return;
+  nudge_view_->parent()->RemoveChildViewT(nudge_view_);
+  nudge_view_ = nullptr;
+}
+
+void DisplayOverlayController::OnNudgeDismissed() {
+  touch_injector_->set_first_launch(false);
+  RemoveNudgeView();
+}
+
+gfx::Point DisplayOverlayController::CalculateNudgePosition(int nudge_width) {
+  gfx::Point nudge_position = CalculateMenuEntryPosition();
+  return gfx::Point(nudge_position.x() - nudge_width - kMenuEntrySideMargin,
+                    nudge_position.y() + kNudgeVerticalAlign);
+}
+
 void DisplayOverlayController::AddMenuEntryView(views::Widget* overlay_widget) {
   if (menu_entry_)
     return;
@@ -213,7 +265,6 @@
 }
 
 void DisplayOverlayController::OnEducationalViewDismissed() {
-  touch_injector_->set_first_launch(false);
   SetDisplayMode(DisplayMode::kView);
 }
 
@@ -274,6 +325,7 @@
       RemoveMenuEntryView();
       RemoveInputMappingView();
       RemoveEditModeExitView();
+      RemoveNudgeView();
       break;
     case DisplayMode::kEducation:
       AddEducationalView();
@@ -284,8 +336,11 @@
       RemoveInputMenuView();
       RemoveEditModeExitView();
       RemoveEducationalView();
+      RemoveNudgeView();
       AddInputMappingView(overlay_widget);
       AddMenuEntryView(overlay_widget);
+      if (touch_injector_->first_launch())
+        AddNudgeView(overlay_widget);
       overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
           aura::EventTargetingPolicy::kNone);
       break;
@@ -293,6 +348,7 @@
       RemoveInputMenuView();
       RemoveMenuEntryView();
       RemoveEducationalView();
+      RemoveNudgeView();
       AddEditModeExitView(overlay_widget);
       overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
           aura::EventTargetingPolicy::kTargetAndDescendants);
@@ -445,7 +501,7 @@
 
 void DisplayOverlayController::ProcessPressedEvent(
     const ui::LocatedEvent& event) {
-  if (!action_edit_menu_ && !error_ && !input_menu_view_)
+  if (!action_edit_menu_ && !error_ && !input_menu_view_ && !nudge_view_)
     return;
 
   auto root_location = event.root_location();
@@ -467,6 +523,10 @@
     if (!bounds.Contains(root_location))
       SetDisplayMode(DisplayMode::kView);
   }
+
+  // Dismiss the nudge, regardless where the click was.
+  if (nudge_view_)
+    OnNudgeDismissed();
 }
 
 void DisplayOverlayController::DismissEducationalViewForTesting() {
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
index 9fbf917..614ed8c 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -22,6 +22,10 @@
 class Widget;
 }  // namespace views
 
+namespace ash {
+class PillButton;
+}  // namespace ash
+
 namespace arc {
 class ArcInputOverlayManagerTest;
 namespace input_overlay {
@@ -85,6 +89,12 @@
   void AddOverlay(DisplayMode display_mode);
   void RemoveOverlayIfAny();
 
+  // On charge of Add/Remove nudge view.
+  void AddNudgeView(views::Widget* overlay_widget);
+  void RemoveNudgeView();
+  void OnNudgeDismissed();
+  gfx::Point CalculateNudgePosition(int nudge_width);
+
   void AddMenuEntryView(views::Widget* overlay_widget);
   void RemoveMenuEntryView();
   void OnMenuEntryPressed();
@@ -133,6 +143,7 @@
   raw_ptr<EditModeExitView> edit_mode_view_ = nullptr;
   raw_ptr<ErrorView> error_ = nullptr;
   raw_ptr<EducationalView> educational_view_ = nullptr;
+  raw_ptr<ash::PillButton> nudge_view_ = nullptr;
 
   DisplayMode display_mode_ = DisplayMode::kNone;
 };
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 4effeef..8cc7b92 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -154,6 +154,13 @@
   kMaxValue = kForcedByPolicyLacrosOnly
 };
 
+// Resources file sharing mode.
+enum class ResourcesFileSharingMode {
+  kDefault = 0,
+  // Failed to handle cached shared resources properly.
+  kError = 1,
+};
+
 using LaunchParamsFromBackground = BrowserManager::LaunchParamsFromBackground;
 
 // Pointer to the global instance of BrowserManager.
@@ -195,22 +202,20 @@
   return false;
 }
 
-// Return false when there is a failure that might break resource file sharing
-// feature.
-bool ClearOrMoveSharedResourceFile(bool clear_shared_resource_file) {
-  base::FilePath shared_resource_path =
-      browser_util::GetUserDataDir().Append(crosapi::kSharedResourcesPackName);
+ResourcesFileSharingMode ClearOrMoveSharedResourceFileInternal(
+    bool clear_shared_resource_file,
+    base::FilePath shared_resource_path) {
   // If shared resource pak doesn't exit, do nothing.
   if (!base::PathExists(shared_resource_path))
-    return true;
+    return ResourcesFileSharingMode::kDefault;
 
   // Clear shared resource file cache if `clear_shared_resource_file` is true.
   if (clear_shared_resource_file) {
     if (!base::DeleteFile(shared_resource_path)) {
       LOG(ERROR) << "Failed to delete cached shared resource file.";
-      return false;
+      return ResourcesFileSharingMode::kError;
     }
-    return true;
+    return ResourcesFileSharingMode::kDefault;
   }
 
   base::FilePath renamed_shared_resource_path =
@@ -220,9 +225,41 @@
   if (!base::Move(shared_resource_path, renamed_shared_resource_path)) {
     LOG(ERROR) << "Failed to move cached shared resource file to temporary "
                << "location.";
-    return false;
+    return ResourcesFileSharingMode::kError;
   }
-  return true;
+  return ResourcesFileSharingMode::kDefault;
+}
+
+ResourcesFileSharingMode ClearOrMoveSharedResourceFile(
+    bool clear_shared_resource_file) {
+  // Check 3 resource paks, resources.pak, chrome_100_percent.pak and
+  // chrome_200_percent.pak.
+  ResourcesFileSharingMode resources_file_sharing_mode =
+      ResourcesFileSharingMode::kDefault;
+  // Return kError if any of the resources failed to clear or move.
+  // Make sure that ClearOrMoveSharedResourceFileInternal() runs for all
+  // resources even if it already fails for some resource.
+  if (ClearOrMoveSharedResourceFileInternal(
+          clear_shared_resource_file, browser_util::GetUserDataDir().Append(
+                                          crosapi::kSharedResourcesPackName)) ==
+      ResourcesFileSharingMode::kError) {
+    resources_file_sharing_mode = ResourcesFileSharingMode::kError;
+  }
+  if (ClearOrMoveSharedResourceFileInternal(
+          clear_shared_resource_file,
+          browser_util::GetUserDataDir().Append(
+              crosapi::kSharedChrome100PercentPackName)) ==
+      ResourcesFileSharingMode::kError) {
+    resources_file_sharing_mode = ResourcesFileSharingMode::kError;
+  }
+  if (ClearOrMoveSharedResourceFileInternal(
+          clear_shared_resource_file,
+          browser_util::GetUserDataDir().Append(
+              crosapi::kSharedChrome200PercentPackName)) ==
+      ResourcesFileSharingMode::kError) {
+    resources_file_sharing_mode = ResourcesFileSharingMode::kError;
+  }
+  return resources_file_sharing_mode;
 }
 
 // This method runs some work on a background thread prior to launching lacros.
@@ -265,7 +302,8 @@
   // Clear shared resource file cache if it's initial lacros launch after ash
   // reboot. If not, rename shared resource file cache to temporal name on
   // Lacros launch.
-  if (!ClearOrMoveSharedResourceFile(clear_shared_resource_file))
+  if (ClearOrMoveSharedResourceFile(clear_shared_resource_file) ==
+      ResourcesFileSharingMode::kError)
     params.enable_resource_file_sharing = false;
 
   return params;
diff --git a/chrome/browser/ash/crosapi/environment_provider.cc b/chrome/browser/ash/crosapi/environment_provider.cc
index 92128cee..34e5c33 100644
--- a/chrome/browser/ash/crosapi/environment_provider.cc
+++ b/chrome/browser/ash/crosapi/environment_provider.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/crosapi/mojom/policy_namespace.mojom.h"
 #include "chromeos/dbus/cros_disks/cros_disks_client.h"
@@ -130,6 +131,11 @@
   default_paths->share_cache =
       file_manager::util::GetShareCacheFilePath(profile);
 
+  default_paths->preinstalled_web_app_config =
+      web_app::GetPreinstalledWebAppConfigDirFromCommandLine(profile);
+  default_paths->preinstalled_web_app_extra_config =
+      web_app::GetPreinstalledWebAppExtraConfigDirFromCommandLine(profile);
+
   return default_paths;
 }
 
diff --git a/chrome/browser/ash/dbus/encrypted_reporting_service_provider.cc b/chrome/browser/ash/dbus/encrypted_reporting_service_provider.cc
index 868cbe5..287607f 100644
--- a/chrome/browser/ash/dbus/encrypted_reporting_service_provider.cc
+++ b/chrome/browser/ash/dbus/encrypted_reporting_service_provider.cc
@@ -172,9 +172,7 @@
   }
 
   upload_provider_->RequestUploadEncryptedRecords(
-      request.need_encryption_keys(),
-      std::make_unique<std::vector<reporting::EncryptedRecord>>(
-          std::move(records)),
+      request.need_encryption_keys(), std::move(records),
       base::BindPostTask(
           origin_thread_runner_,
           base::BindOnce(&SendStatusAsResponse, std::move(response),
diff --git a/chrome/browser/ash/drive/OWNERS b/chrome/browser/ash/drive/OWNERS
index ce1c9d0..31b8eee 100644
--- a/chrome/browser/ash/drive/OWNERS
+++ b/chrome/browser/ash/drive/OWNERS
@@ -1,8 +1 @@
-austinct@chromium.org
-hashimoto@chromium.org
-hidehiko@chromium.org
-hirono@chromium.org
-kinaba@chromium.org
-sammc@chromium.org
-slangley@chromium.org
-yoshiki@chromium.org
+file://ui/file_manager/OWNERS
\ No newline at end of file
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index 284875fd..d0fea72 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/files/file_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/file_manager/fileapi_util.h"
@@ -21,6 +22,10 @@
 namespace file_manager {
 namespace io_task {
 
+void RecordUmaExtractStatus(ExtractStatus status) {
+  UMA_HISTOGRAM_ENUMERATION(kExtractTaskStatusHistogramName, status);
+}
+
 ExtractIOTask::ExtractIOTask(
     std::vector<storage::FileSystemURL> source_urls,
     storage::FileSystemURL parent_folder,
@@ -52,6 +57,9 @@
   progress_.state = success ? State::kSuccess : State::kError;
   DCHECK_GT(extractCount_, 0);
   if (--extractCount_ == 0) {
+    RecordUmaExtractStatus(progress_.state == State::kSuccess
+                               ? ExtractStatus::kSuccess
+                               : ExtractStatus::kUnknownError);
     Complete();
   }
 }
@@ -118,6 +126,7 @@
     progress_.outputs.emplace_back(progress_.destination_folder,
                                    base::File::FILE_ERROR_NO_SPACE);
     progress_.state = State::kError;
+    RecordUmaExtractStatus(ExtractStatus::kInsufficientDiskSpace);
     Complete();
     return;
   }
@@ -175,6 +184,7 @@
   if (!chromeos::FileSystemBackend::CanHandleURL(parent_folder_) ||
       sizingCount_ == 0) {
     progress_.state = State::kError;
+    RecordUmaExtractStatus(ExtractStatus::kUnknownError);
     Complete();
   } else {
     CheckSizeThenExtract();
@@ -183,6 +193,7 @@
 
 void ExtractIOTask::Cancel() {
   progress_.state = State::kCancelled;
+  RecordUmaExtractStatus(ExtractStatus::kCancelled);
   // Any inflight operation will be cancelled when the task is destroyed.
 }
 
diff --git a/chrome/browser/ash/file_manager/extract_io_task.h b/chrome/browser/ash/file_manager/extract_io_task.h
index 3466c78..6dfd098 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.h
+++ b/chrome/browser/ash/file_manager/extract_io_task.h
@@ -21,6 +21,22 @@
 namespace file_manager {
 namespace io_task {
 
+// Histogram name for FileBrowser.ExtractTask.
+inline constexpr char kExtractTaskStatusHistogramName[] =
+    "FileBrowser.ExtractTask.Status";
+
+// Extract archive status. These values are persisted to logs. Entries should
+// not be renumbered and numeric values should never be reused.
+// See enum FileManagerExtractStatus in enums.xml.
+enum class ExtractStatus {
+  kSuccess = 0,
+  kUnknownError = 1,
+  kCancelled = 2,
+  kInsufficientDiskSpace = 3,
+  kPasswordError = 4,
+  kMaxValue = kPasswordError,
+};
+
 class ExtractIOTask : public IOTask {
  public:
   // Create a task to extract any ZIP files in |source_urls|. These
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
index 49fcffb..fddcae6 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
@@ -243,6 +243,7 @@
     easy_unlock_service_regular_->Shutdown();
     PowerManagerClient::Shutdown();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
+    display::Screen::SetScreenInstance(nullptr);
   }
 
   // Most tests will want to pass `should_initialize_all_dependencies` == true,
diff --git a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
index 06ceeb4..7777995 100644
--- a/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/consolidated_consent_screen_browsertest.cc
@@ -104,21 +104,22 @@
 // Google EUlA Dialog
 const test::UIPath kGoogleEulaDialog = {kConsolidatedConsentId,
                                         "googleEulaDialog"};
-const test::UIPath kGoogleEulaWebview = {kConsolidatedConsentId,
-                                         "googleEulaWebview"};
+const test::UIPath kGoogleEulaWebview = {
+    kConsolidatedConsentId, "consolidatedConsentGoogleEulaWebview"};
 const test::UIPath kGoogleEulaOkButton = {kConsolidatedConsentId,
                                           "googleEulaOkButton"};
 
 // CROS EULA Dialog
 const test::UIPath kCrosEulaDialog = {kConsolidatedConsentId, "crosEulaDialog"};
 const test::UIPath kCrosEulaWebview = {kConsolidatedConsentId,
-                                       "crosEulaWebview"};
+                                       "consolidatedConsentCrosEulaWebview"};
 const test::UIPath kCrosEulaOkButton = {kConsolidatedConsentId,
                                         "crosEulaOkButton"};
 
 // ARC ToS Dialog
 const test::UIPath kArcTosDialog = {kConsolidatedConsentId, "arcTosDialog"};
-const test::UIPath kArcTosWebview = {kConsolidatedConsentId, "arcTosWebview"};
+const test::UIPath kArcTosWebview = {kConsolidatedConsentId,
+                                     "consolidatedConsentArcTosWebview"};
 const test::UIPath kArcTosOkButton = {kConsolidatedConsentId, "arcTosOkButton"};
 
 // Privacy Policy Dialog
diff --git a/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc b/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
index f07c450..2ea88a88 100644
--- a/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/guest_tos_screen_browsertest.cc
@@ -39,12 +39,13 @@
 
 // Google EUlA Dialog
 const test::UIPath kGoogleEulaDialog = {kGuestTostId, "googleEulaDialog"};
-const test::UIPath kGoogleEulaWebview = {kGuestTostId, "googleEulaWebview"};
+const test::UIPath kGoogleEulaWebview = {kGuestTostId,
+                                         "guestTosGoogleEulaWebview"};
 const test::UIPath kGoogleEulaOkButton = {kGuestTostId, "googleEulaOkButton"};
 
 // CROS EULA Dialog
 const test::UIPath kCrosEulaDialog = {kGuestTostId, "crosEulaDialog"};
-const test::UIPath kCrosEulaWebview = {kGuestTostId, "crosEulaWebview"};
+const test::UIPath kCrosEulaWebview = {kGuestTostId, "guestTosCrosEulaWebview"};
 const test::UIPath kCrosEulaOkButton = {kGuestTostId, "crosEulaOkButton"};
 
 }  // namespace
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
index 7800ccc6..808233f 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.cc
@@ -78,6 +78,18 @@
   return uri.GetNormalized(false);
 }
 
+// Calls `callback`. Adds a prefix with `context` to an error sent in `data`.
+void PrefixForError(StatusCallback callback,
+                    const std::string& context,
+                    StatusCode status,
+                    const std::string& data) {
+  std::string msg = data;
+  if (status != StatusCode::kOK) {
+    msg = "[" + context + "] " + data;
+  }
+  std::move(callback).Run(status, msg);
+}
+
 }  // namespace
 
 AuthorizationZoneImpl::WaitingAuthorization::WaitingAuthorization(
@@ -104,6 +116,7 @@
 void AuthorizationZoneImpl::InitAuthorization(const std::string& scope,
                                               StatusCallback callback) {
   DCHECK_LE(waiting_authorizations_.size(), kMaxNumberOfSessions);
+  AddContextToErrorMessage(callback);
 
   // If there are too many callbacks waiting, remove the oldest one.
   if (waiting_authorizations_.size() == kMaxNumberOfSessions) {
@@ -132,6 +145,36 @@
   }
 }
 
+void AuthorizationZoneImpl::FinishAuthorization(const GURL& redirect_url,
+                                                StatusCallback callback) {
+  AddContextToErrorMessage(callback);
+  // TODO(pawliczek)
+  // This method is supposed to parse `redirect_url`, then match
+  // PendingAuthorization structure using the `state` field and finalize
+  // the authorization on the server side by obtaining the first access token.
+}
+
+void AuthorizationZoneImpl::GetEndpointAccessToken(
+    const chromeos::Uri& ipp_endpoint,
+    const std::string& scope,
+    StatusCallback callback) {
+  AddContextToErrorMessage(callback);
+  // TODO(pawliczek)
+  // This method is supposed to return endpoint access token for given
+  // `ipp_endpoint`. If the given `ipp_endpoint` is not known yet, this method
+  // will request from the server a new endpoint access token for given
+  // `ipp_endpoint` and `scope`.
+}
+
+void AuthorizationZoneImpl::MarkEndpointAccessTokenAsExpired(
+    const chromeos::Uri& ipp_endpoint,
+    const std::string& endpoint_access_token) {
+  // TODO(pawliczek)
+  // This method removes given `ipp_endpoint` from the list of known ipp
+  // endpoints. It happens only if its endpoint access token equals
+  // `endpoint_access_token`.
+}
+
 void AuthorizationZoneImpl::AuthorizationProcedure() {
   DCHECK_LE(pending_authorizations_.size(), kMaxNumberOfSessions);
 
@@ -165,6 +208,13 @@
   waiting_authorizations_.clear();
 }
 
+void AuthorizationZoneImpl::AddContextToErrorMessage(StatusCallback& callback) {
+  // Wrap the `callback` with the function PrefixForError(...) defined above.
+  const std::string prefix = server_data_.AuthorizationServerURI().spec();
+  auto new_call = base::BindOnce(&PrefixForError, std::move(callback), prefix);
+  callback = std::move(new_call);
+}
+
 std::unique_ptr<AuthorizationZone> AuthorizationZone::Create(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const GURL& authorization_server_uri,
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
index 424cfae7..f0c54a7 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_impl.h
@@ -47,15 +47,15 @@
                          StatusCallback callback) override;
   // AuthorizationZone interface.
   void FinishAuthorization(const GURL& redirect_url,
-                           StatusCallback callback) override {}
+                           StatusCallback callback) override;
   // AuthorizationZone interface.
   void GetEndpointAccessToken(const chromeos::Uri& ipp_endpoint,
                               const std::string& scope,
-                              StatusCallback callback) override {}
+                              StatusCallback callback) override;
   // AuthorizationZone interface.
   void MarkEndpointAccessTokenAsExpired(
       const chromeos::Uri& ipp_endpoint,
-      const std::string& endpoint_access_token) override {}
+      const std::string& endpoint_access_token) override;
 
  private:
   // This method processes (and removes) all elements from
@@ -65,6 +65,9 @@
   // Callback for AuthorizationServerData::Initialize(...).
   void OnInitializeCallback(StatusCode status, const std::string& data);
 
+  // Adds context info to error messages returned with `callback`.
+  void AddContextToErrorMessage(StatusCallback& callback);
+
   // Represents started authorization procedure waiting for opening
   // communication with the server. This object is created when
   // InitAuthorization(...) is called and its callback does not return yet.
diff --git a/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc b/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
index 3db414b..f21338f 100644
--- a/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
+++ b/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc
@@ -128,6 +128,21 @@
   }
 }
 
+TEST_F(PrintingOAuth2AuthorizationZoneTest, PrefixInErrorMessage) {
+  CallbackResult cr;
+  CreateAuthorizationZone("");
+
+  // Respond with error to Metadata Request.
+  authorization_zone_->InitAuthorization("", BindResult(cr));
+  server_.ReceiveGET(metadata_uri_);
+  server_.ResponseWithJSON(net::HttpStatusCode::HTTP_INTERNAL_SERVER_ERROR, {});
+
+  // Check if the error message begins with the URI of the server.
+  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kServerError);
+  const std::string msg_prefix = "[" + authorization_server_uri_ + "]";
+  EXPECT_EQ(cr.data.substr(0, msg_prefix.length()), msg_prefix);
+}
+
 }  // namespace
 }  // namespace oauth2
 }  // namespace printing
diff --git a/chrome/browser/ash/remote_apps/remote_apps_manager.cc b/chrome/browser/ash/remote_apps/remote_apps_manager.cc
index 97814ab..4f41decc 100644
--- a/chrome/browser/ash/remote_apps/remote_apps_manager.cc
+++ b/chrome/browser/ash/remote_apps/remote_apps_manager.cc
@@ -203,7 +203,7 @@
     DCHECK_EQ(sync_pb::AppListSpecifics::TYPE_FOLDER, sync_item->item_type);
     remote_folder->SetMetadata(
         app_list::GenerateItemMetadataFromSyncItem(*sync_item));
-    remote_folder->SetIsPersistent(true);
+    remote_folder->SetIsSystemFolder(true);
     app_list_syncable_service_->AddItem(std::move(remote_folder));
     return;
   }
@@ -212,7 +212,7 @@
   DCHECK(model_->HasFolder(folder_id));
   const RemoteAppsModel::FolderInfo& info = model_->GetFolderInfo(folder_id);
   remote_folder->SetChromeName(info.name);
-  remote_folder->SetIsPersistent(true);
+  remote_folder->SetIsSystemFolder(true);
   remote_folder->SetChromeIsFolder(true);
   syncer::StringOrdinal position =
       info.add_to_front ? model_updater_->GetPositionBeforeFirstItem()
diff --git a/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn b/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
index 8d5ce21..5beceec 100644
--- a/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
+++ b/chrome/browser/ash/system_extensions/api/window_management/BUILD.gn
@@ -33,6 +33,7 @@
 
   deps = [
     ":window_management",
+    "//chrome/browser/ash/system_extensions",
     "//chrome/browser/web_applications:web_applications_test_support",
     "//chrome/test:test_support",
   ]
diff --git a/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc b/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
index 6ddf38a2..b29b43bc 100644
--- a/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
+++ b/chrome/browser/ash/system_extensions/api/window_management/cros_window_browsertest.cc
@@ -4,20 +4,31 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.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/system_extensions/system_extensions_install_manager.h"
+#include "chrome/browser/ash/system_extensions/system_extensions_provider.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_installation.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/console_message.h"
+#include "content/public/browser/service_worker_context.h"
+#include "content/public/browser/service_worker_context_observer.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -27,6 +38,8 @@
 
 namespace {
 
+constexpr SystemExtensionId kTestSystemExtensionId = {1, 2, 3, 4};
+
 static constexpr char kEventListenerCode[] = R"(
   self.addEventListener('message', async (event) => {
     try {
@@ -58,6 +71,60 @@
   }
 )";
 
+// Used to wait for a message to get added to the Service Worker console.
+// Returns the first message added to the console.
+class ServiceWorkerConsoleObserver
+    : public content::ServiceWorkerContextObserver {
+ public:
+  ServiceWorkerConsoleObserver(Profile* profile, const GURL& scope)
+      : profile_(profile), scope_(scope) {
+    auto* worker_context =
+        profile->GetDefaultStoragePartition()->GetServiceWorkerContext();
+    worker_context->AddObserver(this);
+  }
+  ~ServiceWorkerConsoleObserver() override = default;
+
+  // Get the first message added to the console since the observer was
+  // constructed. Will wait if there are no messages yet.
+  const std::u16string& WaitAndGetNextConsoleMessage() {
+    if (!message_.has_value())
+      run_loop_.Run();
+
+    return message_.value();
+  }
+
+  void OnReportConsoleMessage(int64_t version_id,
+                              const GURL& scope,
+                              const content::ConsoleMessage& message) override {
+    if (scope != scope_)
+      return;
+
+    auto* worker_context =
+        profile_->GetDefaultStoragePartition()->GetServiceWorkerContext();
+    worker_context->RemoveObserver(this);
+
+    // Shouldn't happen because we unregistered as observers.
+    DCHECK(!message_.has_value());
+
+    message_ = message.message;
+    run_loop_.Quit();
+  }
+
+ private:
+  Profile* const profile_;
+  const GURL scope_;
+
+  absl::optional<std::u16string> message_;
+  base::RunLoop run_loop_;
+};
+
+base::FilePath GetWindowManagerExtensionDir() {
+  base::FilePath test_dir;
+  base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir);
+  return test_dir.Append("system_extensions")
+      .Append("window_manager_extension");
+}
+
 class CrosWindowBrowserTest : public InProcessBrowserTest {
  public:
   CrosWindowBrowserTest() {
@@ -129,6 +196,19 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+class CrosWindowExtensionBrowserTest : public InProcessBrowserTest {
+ public:
+  CrosWindowExtensionBrowserTest() {
+    feature_list_.InitWithFeatures(
+        {features::kSystemExtensions,
+         ::features::kEnableServiceWorkersForChromeUntrusted},
+        {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 }  // namespace
 
 IN_PROC_BROWSER_TEST_F(CrosWindowBrowserTest, CrosWindowMoveTo) {
@@ -562,4 +642,29 @@
   RunTest(test_code);
 }
 
+IN_PROC_BROWSER_TEST_F(CrosWindowExtensionBrowserTest, StartEvent) {
+  auto* provider = SystemExtensionsProvider::Get(browser()->profile());
+  auto& install_manager = provider->install_manager();
+
+  // TODO(b/230811571): Rather than using the console to wait for the
+  // observer to get called, we should add support for running async functions
+  // to content::ServiceWorkerContext::ExecuteScriptForTest.
+  ServiceWorkerConsoleObserver sw_console_observer(
+      browser()->profile(),
+      GURL("chrome-untrusted://system-extension-echo-01020304/"));
+
+  base::RunLoop run_loop;
+  install_manager.InstallUnpackedExtensionFromDir(
+      GetWindowManagerExtensionDir(),
+      base::BindLambdaForTesting([&](InstallStatusOrSystemExtensionId result) {
+        ASSERT_TRUE(result.ok());
+        ASSERT_EQ(kTestSystemExtensionId, result.value());
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  EXPECT_EQ(u"start event fired",
+            sw_console_observer.WaitAndGetNextConsoleMessage());
+}
+
 }  //  namespace ash
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
index 99a3ba1..81e4a5b 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.cc
@@ -25,7 +25,9 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/webui_config_map.h"
 #include "content/public/common/url_constants.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.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_options.mojom.h"
 #include "url/gurl.h"
@@ -177,6 +179,53 @@
 
   for (auto& observer : observers_)
     observer.OnServiceWorkerRegistered(system_extension_id, status_code);
+
+  auto it = system_extensions_.find(system_extension_id);
+  if (it == system_extensions_.end()) {
+    LOG(ERROR) << "Tried to start service worker for non-existent extension";
+    return;
+  }
+
+  const SystemExtension& system_extension = it->second;
+  const GURL& scope = system_extension.base_url;
+
+  // TODO(b/221123297): Only dispatch `start` event for window manager
+  // system extensions. This is OK for now, because we only have window
+  // manager extensions.
+  auto* worker_context =
+      profile_->GetDefaultStoragePartition()->GetServiceWorkerContext();
+  worker_context->StartWorkerForScope(
+      scope, blink::StorageKey(url::Origin::Create(scope)),
+      base::BindOnce(
+          &SystemExtensionsInstallManager::DispatchWindowManagerStartEvent,
+          weak_ptr_factory_.GetWeakPtr(), system_extension_id),
+      base::BindOnce([](blink::ServiceWorkerStatusCode status_code) {
+        LOG(ERROR) << "Failed to start service worker: "
+                   << blink::ServiceWorkerStatusToString(status_code);
+      }));
+}
+
+void SystemExtensionsInstallManager::DispatchWindowManagerStartEvent(
+    const SystemExtensionId& system_extension_id,
+    int64_t version_id,
+    int process_id,
+    int thread_id) {
+  auto it = system_extensions_.find(system_extension_id);
+  if (it == system_extensions_.end()) {
+    LOG(ERROR) << "Tried to dispatch event to service worker for "
+               << "non-existent extension";
+    return;
+  }
+
+  auto* worker_context =
+      profile_->GetDefaultStoragePartition()->GetServiceWorkerContext();
+  auto* remote_interfaces = worker_context->GetRemoteInterfaces(version_id);
+  if (!remote_interfaces)
+    return;
+
+  mojo::Remote<blink::mojom::CrosWindowManagementStartObserver> observer;
+  remote_interfaces->GetInterface(observer.BindNewPipeAndPassReceiver());
+  observer->DispatchStartEvent();
 }
 
 bool SystemExtensionsInstallManager::IOHelper::CopyExtensionAssets(
diff --git a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
index cff8920..ec76073 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
+++ b/chrome/browser/ash/system_extensions/system_extensions_install_manager.h
@@ -86,6 +86,10 @@
   void RegisterServiceWorker(const SystemExtensionId& id);
   void OnRegisterServiceWorker(const SystemExtensionId& id,
                                blink::ServiceWorkerStatusCode status_code);
+  void DispatchWindowManagerStartEvent(const SystemExtensionId& id,
+                                       int64_t version_id,
+                                       int process_id,
+                                       int thread_id);
 
   Profile* profile_;
 
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index 90fc5b6..e6ffe0c 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -45,6 +45,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/entry_info.h"
+#include "media/base/media_switches.h"
 #include "services/media_session/public/mojom/media_controller.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
@@ -105,6 +106,14 @@
     feature_list_.InitAndEnableFeature(ash::features::kMediaAppHandlesPdf);
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SystemWebAppIntegrationTest::SetUpCommandLine(command_line);
+
+    // Use a fake audio stream. Some tests make noise otherwise, and could fight
+    // with parallel tests for access to the audio device.
+    command_line->AppendSwitch(switches::kDisableAudioOutput);
+  }
+
   void MediaAppLaunchWithFile();
   void MediaAppWithLaunchSystemWebAppAsync();
   void MediaAppEligibleOpenTask();
@@ -362,9 +371,9 @@
   // Launch the App for the first time.
   web_app::SystemAppLaunchParams audio_params;
   audio_params.launch_paths.push_back(TestFile(kFilePng800x600));
-  web_app::LaunchSystemWebAppAsync(browser()->profile(),
-                                   ash::SystemWebAppType::MEDIA, audio_params);
-  web_app::FlushSystemWebAppLaunchesForTesting(browser()->profile());
+  web_app::LaunchSystemWebAppAsync(profile(), ash::SystemWebAppType::MEDIA,
+                                   audio_params);
+  web_app::FlushSystemWebAppLaunchesForTesting(profile());
   Browser* first_browser = chrome::FindBrowserWithActiveWindow();
   content::WebContents* app = PrepareActiveBrowserForTest();
 
@@ -373,9 +382,9 @@
   // Launch the App for the second time.
   web_app::SystemAppLaunchParams image_params;
   image_params.launch_paths.push_back(TestFile(kFileJpeg640x480));
-  web_app::LaunchSystemWebAppAsync(browser()->profile(),
-                                   ash::SystemWebAppType::MEDIA, image_params);
-  web_app::FlushSystemWebAppLaunchesForTesting(browser()->profile());
+  web_app::LaunchSystemWebAppAsync(profile(), ash::SystemWebAppType::MEDIA,
+                                   image_params);
+  web_app::FlushSystemWebAppLaunchesForTesting(profile());
   app = PrepareActiveBrowserForTest(3);
   Browser* second_browser = chrome::FindBrowserWithActiveWindow();
 
@@ -390,9 +399,9 @@
   image_params.launch_paths = {TestFile(kFilePng800x600),
                                TestFile(kFileJpeg640x480)};
 
-  web_app::LaunchSystemWebAppAsync(browser()->profile(),
-                                   ash::SystemWebAppType::MEDIA, image_params);
-  web_app::FlushSystemWebAppLaunchesForTesting(browser()->profile());
+  web_app::LaunchSystemWebAppAsync(profile(), ash::SystemWebAppType::MEDIA,
+                                   image_params);
+  web_app::FlushSystemWebAppLaunchesForTesting(profile());
 
   const BrowserList* browser_list = BrowserList::GetInstance();
   EXPECT_EQ(2u, browser_list->size());  // 1 extra for the browser test browser.
@@ -409,12 +418,13 @@
   web_app::SystemAppLaunchParams pdf_params;
   pdf_params.launch_paths = {TestFile(kFilePdfTall), TestFile(kFilePdfImg)};
 
-  web_app::LaunchSystemWebAppAsync(browser()->profile(),
-                                   ash::SystemWebAppType::MEDIA, pdf_params);
-  web_app::FlushSystemWebAppLaunchesForTesting(browser()->profile());
+  web_app::LaunchSystemWebAppAsync(profile(), ash::SystemWebAppType::MEDIA,
+                                   pdf_params);
+  web_app::FlushSystemWebAppLaunchesForTesting(profile());
 
+  WaitForBrowserCount(3);  // 1 extra for the browser test browser.
   const BrowserList* browser_list = BrowserList::GetInstance();
-  EXPECT_EQ(3u, browser_list->size());  // 1 extra for the browser test browser.
+  EXPECT_EQ(3u, browser_list->size());
 
   content::TitleWatcher watcher1(
       browser_list->get(1)->tab_strip_model()->GetActiveWebContents(),
@@ -429,8 +439,7 @@
 // Test that the Media App appears as a handler for files in the App Service.
 IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, MediaAppHandlesIntents) {
   WaitForTestSystemAppInstall();
-  auto* proxy =
-      apps::AppServiceProxyFactory::GetForProfile(browser()->profile());
+  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
   const std::string media_app_id =
       *GetManager().GetAppIdForSystemApp(ash::SystemWebAppType::MEDIA);
 
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 0cb701b8..341458a 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -56,8 +56,7 @@
                                                      StartupData* startup_data)
     : ChromeBrowserMainPartsPosix(is_integration_test, startup_data) {}
 
-ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() {
-}
+ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() = default;
 
 int ChromeBrowserMainPartsMac::PreEarlyInitialization() {
   if (base::mac::WasLaunchedAsLoginItemRestoreState()) {
@@ -69,7 +68,6 @@
         base::CommandLine::ForCurrentProcess();
     singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
   }
-
   return ChromeBrowserMainPartsPosix::PreEarlyInitialization();
 }
 
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 600097b..dc6f7b0 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -20,6 +20,8 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/dcheck_is_on.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/enterprise_util.h"
 #include "base/environment.h"
 #include "base/feature_list.h"
@@ -44,6 +46,7 @@
 #include "base/win/windows_version.h"
 #include "base/win/wrapped_window_proc.h"
 #include "build/branding_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/util/critical_policy_section_metrics_win.h"
@@ -383,74 +386,6 @@
   return true;
 }
 
-// Used as the callback for ModuleWatcher events in this process. Dispatches
-// them to the ModuleDatabase.
-// Note: This callback may be invoked on any thread, even those not owned by the
-//       task scheduler, under the loader lock, directly on the thread where the
-//       DLL is currently loading.
-void OnModuleEvent(const ModuleWatcher::ModuleEvent& event) {
-  {
-    TRACE_EVENT1("browser", "OnModuleEvent", "module_path",
-                 event.module_path.BaseName().AsUTF8Unsafe());
-
-    switch (event.event_type) {
-      case ModuleWatcher::ModuleEventType::kModuleAlreadyLoaded: {
-        // kModuleAlreadyLoaded comes from the enumeration of loaded modules
-        // using CreateToolhelp32Snapshot().
-        uint32_t time_date_stamp = 0;
-        if (TryGetModuleTimeDateStamp(event.module_load_address,
-                                      event.module_path, event.module_size,
-                                      &time_date_stamp)) {
-          ModuleDatabase::HandleModuleLoadEvent(
-              content::PROCESS_TYPE_BROWSER, event.module_path,
-              event.module_size, time_date_stamp);
-        } else {
-          // Failed to get the TimeDateStamp directly from memory. The next step
-          // to try is to read the file on disk. This must be done in a blocking
-          // task.
-          base::ThreadPool::PostTask(
-              FROM_HERE,
-              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-              base::BindOnce(&HandleModuleLoadEventWithoutTimeDateStamp,
-                             event.module_path, event.module_size));
-        }
-        break;
-      }
-      case ModuleWatcher::ModuleEventType::kModuleLoaded: {
-        ModuleDatabase::HandleModuleLoadEvent(
-            content::PROCESS_TYPE_BROWSER, event.module_path, event.module_size,
-            GetModuleTimeDateStamp(event.module_load_address));
-        break;
-      }
-    }
-  }
-  // Since OnModuleEvent can be invoked from any thread, the above trace event's
-  // END might be the last event on this thread, emit an empty event to force
-  // the END to be flushed. TODO(crbug.com/1021571): Remove this once fixed.
-  PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
-}
-
-// Helper function for initializing the module database subsystem and populating
-// the provided |module_watcher|.
-void SetupModuleDatabase(std::unique_ptr<ModuleWatcher>* module_watcher) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(module_watcher);
-
-  bool third_party_blocking_policy_enabled =
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-      ModuleDatabase::IsThirdPartyBlockingPolicyEnabled();
-#else
-      false;
-#endif
-
-  ModuleDatabase::GetTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&InitializeModuleDatabase,
-                                third_party_blocking_policy_enabled));
-
-  *module_watcher = ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent));
-}
-
 void ShowCloseBrowserFirstMessageBox() {
   chrome::ShowWarningMessageBox(
       nullptr, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
@@ -663,6 +598,18 @@
             .get());
 #endif
 
+#if DCHECK_IS_ON()
+    // Patching EAT of kernel32.dll is only supported on 32-bit because RVA can
+    // only hold 32-bit values.
+#if defined(ARCH_CPU_32_BITS)
+  base::debug::HandleHooks::AddEATPatch();
+#endif
+  // Patch currently loaded modules. Future ones will get patched by the module
+  // watcher. Note: if any modules load between now and when SetupModuleDatabase
+  // is called then these will be missed.
+  base::debug::HandleHooks::PatchLoadedModules();
+#endif  // DCHECK_IS_ON()
+
   // Create the module database and hook up the in-process module watcher. This
   // needs to be done before any child processes are initialized as the
   // ModuleDatabase is an endpoint for IPC from child processes.
@@ -934,3 +881,82 @@
   // duplicates, perhaps harmonize with switches::RemoveSwitchesForAutostart.
   return restart_command;
 }
+
+// Used as the callback for ModuleWatcher events in this process. Dispatches
+// them to the ModuleDatabase.
+// Note: This callback may be invoked on any thread, even those not owned by the
+//       task scheduler, under the loader lock, directly on the thread where the
+//       DLL is currently loading.
+void ChromeBrowserMainPartsWin::OnModuleEvent(
+    const ModuleWatcher::ModuleEvent& event) {
+  {
+    TRACE_EVENT1("browser", "OnModuleEvent", "module_path",
+                 event.module_path.BaseName().AsUTF8Unsafe());
+
+    switch (event.event_type) {
+      case ModuleWatcher::ModuleEventType::kModuleAlreadyLoaded: {
+        // kModuleAlreadyLoaded comes from the enumeration of loaded modules
+        // using CreateToolhelp32Snapshot().
+        uint32_t time_date_stamp = 0;
+        if (TryGetModuleTimeDateStamp(event.module_load_address,
+                                      event.module_path, event.module_size,
+                                      &time_date_stamp)) {
+          ModuleDatabase::HandleModuleLoadEvent(
+              content::PROCESS_TYPE_BROWSER, event.module_path,
+              event.module_size, time_date_stamp);
+        } else {
+          // Failed to get the TimeDateStamp directly from memory. The next step
+          // to try is to read the file on disk. This must be done in a blocking
+          // task.
+          base::ThreadPool::PostTask(
+              FROM_HERE,
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+              base::BindOnce(&HandleModuleLoadEventWithoutTimeDateStamp,
+                             event.module_path, event.module_size));
+        }
+        break;
+      }
+      case ModuleWatcher::ModuleEventType::kModuleLoaded: {
+#if DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+        // This is only needed on 64-bit because on 32-bit the EAT from kernel32
+        // is already patched. This is thread safe against itself as this is
+        // always called under loader lock.
+        HMODULE module =
+            reinterpret_cast<HMODULE>(event.module_load_address.get());
+        base::debug::HandleHooks::AddIATPatch(module);
+#endif  // DCHECK_IS_ON() && defined(ARCH_CPU_64_BITS)
+        ModuleDatabase::HandleModuleLoadEvent(
+            content::PROCESS_TYPE_BROWSER, event.module_path, event.module_size,
+            GetModuleTimeDateStamp(event.module_load_address));
+        break;
+      }
+    }
+  }
+  // Since OnModuleEvent can be invoked from any thread, the above trace event's
+  // END might be the last event on this thread, emit an empty event to force
+  // the END to be flushed. TODO(crbug.com/1021571): Remove this once fixed.
+  PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
+}
+
+// Helper function for initializing the module database subsystem and populating
+// the provided |module_watcher|.
+void ChromeBrowserMainPartsWin::SetupModuleDatabase(
+    std::unique_ptr<ModuleWatcher>* module_watcher) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(module_watcher);
+
+  bool third_party_blocking_policy_enabled =
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+      ModuleDatabase::IsThirdPartyBlockingPolicyEnabled();
+#else
+      false;
+#endif
+
+  ModuleDatabase::GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&InitializeModuleDatabase,
+                                third_party_blocking_policy_enabled));
+
+  *module_watcher = ModuleWatcher::Create(base::BindRepeating(
+      &ChromeBrowserMainPartsWin::OnModuleEvent, base::Unretained(this)));
+}
diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h
index 66bcce9..87e802df 100644
--- a/chrome/browser/chrome_browser_main_win.h
+++ b/chrome/browser/chrome_browser_main_win.h
@@ -10,8 +10,7 @@
 #include <memory>
 
 #include "chrome/browser/chrome_browser_main.h"
-
-class ModuleWatcher;
+#include "chrome/common/conflicts/module_watcher_win.h"
 
 namespace base {
 class CommandLine;
@@ -77,6 +76,9 @@
       const base::CommandLine& command_line);
 
  private:
+  void OnModuleEvent(const ModuleWatcher::ModuleEvent& event);
+  void SetupModuleDatabase(std::unique_ptr<ModuleWatcher>* module_watcher);
+
   // Watches module load events and forwards them to the ModuleDatabase.
   std::unique_ptr<ModuleWatcher> module_watcher_;
 };
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 357182f7..99660dd1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -343,6 +343,7 @@
 #include "chrome/browser/browser_process_platform_part_mac.h"
 #include "chrome/browser/chrome_browser_main_mac.h"
 #include "chrome/browser/mac/auth_session_request.h"
+#include "chrome/browser/mac/chrome_browser_main_extra_parts_mac.h"
 #include "components/soda/constants.h"
 #include "sandbox/mac/seatbelt_exec.h"
 #include "sandbox/policy/mac/params.h"
@@ -1453,6 +1454,10 @@
 #endif
 #endif
 
+#if BUILDFLAG(IS_MAC)
+  main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsMac>());
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // TODO(jamescook): Combine with `ChromeBrowserMainPartsAsh`.
   main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsAsh>());
diff --git a/chrome/browser/dev_ui_browser_resources.grd b/chrome/browser/dev_ui_browser_resources.grd
index 255ca4b9..4889cf9 100644
--- a/chrome/browser/dev_ui_browser_resources.grd
+++ b/chrome/browser/dev_ui_browser_resources.grd
@@ -35,7 +35,7 @@
       <include name="IDR_LOCAL_STATE_HTML" file="resources\local_state\local_state.html" type="BINDATA" />
       <include name="IDR_LOCAL_STATE_JS" file="${root_gen_dir}\chrome\browser\resources\local_state\local_state.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_MEMORY_INTERNALS_HTML" file="resources\memory_internals\memory_internals.html" type="BINDATA" />
-      <include name="IDR_MEMORY_INTERNALS_JS" file="resources\memory_internals\memory_internals.js" type="BINDATA" />
+      <include name="IDR_MEMORY_INTERNALS_JS" file="${root_gen_dir}\chrome\browser\resources\memory_internals\tsc\memory_internals.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_PREDICTORS_AUTOCOMPLETE_ACTION_PREDICTOR_JS" file="${root_gen_dir}\chrome\browser\resources\predictors\autocomplete_action_predictor.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_PREDICTORS_HTML" file="resources\predictors\predictors.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_PREDICTORS_JS" file="${root_gen_dir}\chrome\browser\resources\predictors\predictors.js" use_base_dir="false" type="BINDATA" />
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index b9c1c70..4b8fec7 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -2963,7 +2963,7 @@
   const auto result = FetchFromDevToolsWindow("http://www.google.com");
   EXPECT_THAT(result.error,
               ::testing::StartsWith(
-                  "a JavaScript error:\nTypeError: Failed to fetch\n"));
+                  "a JavaScript error: \"TypeError: Failed to fetch\n"));
 
   CloseDevToolsWindow();
 }
@@ -2976,7 +2976,7 @@
             "devtools://devtools/bundled/devtools_compatibility.js");
   EXPECT_THAT(result.error,
               ::testing::StartsWith(
-                  "a JavaScript error:\nTypeError: Failed to fetch\n"));
+                  "a JavaScript error: \"TypeError: Failed to fetch\n"));
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsTest, HostBindingsSyncIntegration) {
diff --git a/chrome/browser/download/download_prefs_unittest.cc b/chrome/browser/download/download_prefs_unittest.cc
index ba12c18..a94ed196 100644
--- a/chrome/browser/download/download_prefs_unittest.cc
+++ b/chrome/browser/download/download_prefs_unittest.cc
@@ -500,9 +500,12 @@
                                                account_id.GetAccountIdKey()));
   const base::FilePath ash_resources_dir = base::FilePath("/opt/google/chrome");
   base::FilePath share_cache_dir = profile.GetPath().AppendASCII("ShareCache");
+  base::FilePath preinstalled_web_app_config_dir;
+  base::FilePath preinstalled_web_app_extra_config_dir;
   chrome::SetLacrosDefaultPaths(
       documents_path, default_dir, drivefs_dir, removable_media_dir,
-      android_files_dir, linux_files_dir, ash_resources_dir, share_cache_dir);
+      android_files_dir, linux_files_dir, ash_resources_dir, share_cache_dir,
+      preinstalled_web_app_config_dir, preinstalled_web_app_extra_config_dir);
 #endif
 
   // Test a valid subdirectory of downloads.
diff --git a/chrome/browser/drive/OWNERS b/chrome/browser/drive/OWNERS
index 2563657..73220a8 100644
--- a/chrome/browser/drive/OWNERS
+++ b/chrome/browser/drive/OWNERS
@@ -1,5 +1 @@
-hashimoto@chromium.org
-hidehiko@chromium.org
-hirono@chromium.org
-kinaba@chromium.org
-yoshiki@chromium.org
+file://ui/file_manager/OWNERS
diff --git a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc
index c87fec7..fc750c7 100644
--- a/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc
+++ b/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_browsertest.cc
@@ -115,7 +115,7 @@
         content::EvalJs(FrameToUseForConnecting(), script);
     EXPECT_THAT(
         result.error,
-        testing::StartsWith("a JavaScript error:\nTypeError: Cannot read "
+        testing::StartsWith("a JavaScript error: \"TypeError: Cannot read "
                             "properties of undefined (reading 'connect')"));
   }
 
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index 2020979..6e8acc6f5 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1081,7 +1081,8 @@
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
   std::unique_ptr<base::ListValue> params = std::make_unique<base::ListValue>();
-  params->Append(enqueue_record_request.ToValue());
+  params->Append(
+      base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
 
   // Set up DM token
   policy::SetDMTokenForTesting(
@@ -1108,7 +1109,8 @@
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
   std::unique_ptr<base::ListValue> params = std::make_unique<base::ListValue>();
-  params->Append(enqueue_record_request.ToValue());
+  params->Append(
+      base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
 
   policy::SetDMTokenForTesting(
       policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
@@ -1136,7 +1138,8 @@
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
   std::unique_ptr<base::ListValue> params = std::make_unique<base::ListValue>();
-  params->Append(enqueue_record_request.ToValue());
+  params->Append(
+      base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
 
   policy::SetDMTokenForTesting(
       policy::DMToken::CreateValidTokenForTesting(kTestDMTokenValue));
@@ -1161,7 +1164,8 @@
       api::enterprise_reporting_private::EventType::EVENT_TYPE_USER;
 
   std::unique_ptr<base::ListValue> params = std::make_unique<base::ListValue>();
-  params->Append(enqueue_record_request.ToValue());
+  params->Append(
+      base::Value::FromUniquePtrValue(enqueue_record_request.ToValue()));
 
   // Set up invalid DM token
   policy::SetDMTokenForTesting(policy::DMToken::CreateInvalidTokenForTesting());
diff --git a/chrome/browser/extensions/api/search/search_api_unittest.cc b/chrome/browser/extensions/api/search/search_api_unittest.cc
index baeaee9..3e69925 100644
--- a/chrome/browser/extensions/api/search/search_api_unittest.cc
+++ b/chrome/browser/extensions/api/search/search_api_unittest.cc
@@ -15,8 +15,6 @@
 #include "content/public/test/web_contents_tester.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension_builder.h"
-#include "ui/display/test/scoped_screen_override.h"
-#include "ui/display/test/test_screen.h"
 
 namespace extensions {
 
@@ -74,8 +72,6 @@
   std::unique_ptr<TestBrowserWindow> browser_window_;
   std::unique_ptr<Browser> browser_;
 
-  display::test::TestScreen test_screen_;
-  std::unique_ptr<display::test::ScopedScreenOverride> scoped_screen_override_;
   scoped_refptr<extensions::SearchQueryFunction> function_;
 };
 
@@ -91,8 +87,6 @@
   params.type = Browser::TYPE_NORMAL;
   params.window = browser_window_.get();
   browser_ = std::unique_ptr<Browser>(Browser::Create(params));
-  scoped_screen_override_ =
-      std::make_unique<display::test::ScopedScreenOverride>(&test_screen_);
 
   // Mock TemplateURLService.
   auto* template_url_service = static_cast<TemplateURLService*>(
diff --git a/chrome/browser/extensions/api/system_display/system_display_extension_apitest.cc b/chrome/browser/extensions/api/system_display/system_display_extension_apitest.cc
index a42a5749..837d0bd 100644
--- a/chrome/browser/extensions/api/system_display/system_display_extension_apitest.cc
+++ b/chrome/browser/extensions/api/system_display/system_display_extension_apitest.cc
@@ -15,16 +15,11 @@
 #include "extensions/browser/api/system_display/system_display_api.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/browser/mock_display_info_provider.h"
-#include "extensions/browser/mock_screen.h"
 #include "extensions/common/api/system_display.h"
 #include "ui/display/display.h"
-#include "ui/display/screen.h"
-#include "ui/display/test/scoped_screen_override.h"
 
 namespace extensions {
 
-using display::Screen;
-using display::test::ScopedScreenOverride;
 using ContextType = ExtensionBrowserTest::ContextType;
 
 class SystemDisplayExtensionApiTest
@@ -39,27 +34,19 @@
 
   void SetUpOnMainThread() override {
     ExtensionApiTest::SetUpOnMainThread();
-    ANNOTATE_LEAKING_OBJECT_PTR(Screen::GetScreen());
-    scoped_screen_override_ =
-        std::make_unique<ScopedScreenOverride>(screen_.get());
     DisplayInfoProvider::InitializeForTesting(provider_.get());
   }
 
   void TearDownOnMainThread() override {
     ExtensionApiTest::TearDownOnMainThread();
-    scoped_screen_override_.reset();
   }
 
  protected:
   std::unique_ptr<MockDisplayInfoProvider> provider_ =
       std::make_unique<MockDisplayInfoProvider>();
-
- private:
-  std::unique_ptr<Screen> screen_ = std::make_unique<MockScreen>();
-  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 };
 
-// TODO(crbug.com/1231357): MockScreen causes random failures on Windows.
+// TODO(crbug.com/1231357): Revisit this after screen creation refactoring.
 #if !BUILDFLAG(IS_WIN)
 
 INSTANTIATE_TEST_SUITE_P(PersistentBackground,
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 005577b..7154cba7 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -33,8 +33,6 @@
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_builder.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/display/test/scoped_screen_override.h"
-#include "ui/display/test/test_screen.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/test/ash_test_helper.h"
@@ -48,8 +46,6 @@
 
 namespace extensions {
 
-using display::test::ScopedScreenOverride;
-
 namespace {
 
 std::unique_ptr<base::ListValue> RunTabsQueryFunction(
@@ -127,13 +123,15 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::AshTestHelper test_helper_;
-#else
-  display::test::TestScreen test_screen_;
-  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 #endif
 };
 
 void TabsApiUnitTest::SetUp() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::AshTestHelper::InitParams ash_params;
+  ash_params.start_session = true;
+  test_helper_.SetUp(std::move(ash_params));
+#endif
   // Force TabManager/TabLifecycleUnitSource creation.
   g_browser_process->GetTabManager();
 
@@ -145,14 +143,6 @@
   params.type = Browser::TYPE_NORMAL;
   params.window = browser_window_.get();
   browser_.reset(Browser::Create(params));
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  ash::AshTestHelper::InitParams ash_params;
-  ash_params.start_session = true;
-  test_helper_.SetUp(std::move(ash_params));
-#else
-  scoped_screen_override_ =
-      std::make_unique<ScopedScreenOverride>(&test_screen_);
-#endif
 }
 
 void TabsApiUnitTest::TearDown() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6172728..723193a 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -779,16 +779,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "certificate-transparency-2022-policy",
-    "owners": [ "carlosil", "asymmetric", "trusty-transport@chromium.org" ],
-    "expiry_milestone": 102
-  },
-  {
-    "name": "certificate-transparency-2022-policy-all-certs",
-    "owners": [ "carlosil", "asymmetric", "trusty-transport@chromium.org" ],
-    "expiry_milestone": 102
-  },
-  {
     "name": "check-offline-capability",
     "owners": [ "asamidoi", "//content/browser/service_worker/OWNERS" ],
     "expiry_milestone": 93
@@ -5382,6 +5372,11 @@
     "expiry_milestone": 105
   },
   {
+    "name": "shared-highlighting-refined-maxcontextwords",
+    "owners": ["wissemgamra", "jeffreycohen", "chrome-with-friends-robots@google.com" ],
+    "expiry_milestone": 107
+  },
+  {
     "name": "shared-highlighting-v2",
     "owners": ["jeffreycohen", "kristipark", "cheickcisse@google.com", "chrome-with-friends-robots@google.com"],
     "expiry_milestone": 99
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0fa48525..bad7bf8 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -576,24 +576,6 @@
     "eligibility requirements for showing app banners, such as having a "
     "manifest, are met.";
 
-const char kCertificateTransparency2022PolicyName[] =
-    "Certificate Transparency 2022 Policy";
-const char kCertificateTransparency2022PolicyDescription[] =
-    "Enables the 2022 policy for Certificate Transparency requirements for "
-    "certificates issued after April 15, 2022. This policy increases the "
-    "number of SCTs required for certificates with a lifetime over 180 days, "
-    "and replaces the one Google log requirement with log operator diversity "
-    "requirements.";
-
-const char kCertificateTransparency2022PolicyAllCertsName[] =
-    "Certificate Transparency 2022 Policy All Certificates";
-const char kCertificateTransparency2022PolicyAllCertsDescription[] =
-    "Enables the 2022 policy for Certificate Transparency requirements for "
-    "all certificates regardless of issuance date. This policy increases the "
-    "number of SCTs required for certificates with a lifetime over 180 days, "
-    "and replaces the one Google log requirement with log operator diversity "
-    "requirements.";
-
 const char kCheckOfflineCapabilityName[] = "Check offline capability for PWAs";
 const char kCheckOfflineCapabilityDescription[] =
     "Use advanced offline capability check to decide whether the browser "
@@ -2918,6 +2900,11 @@
 const char kSharedHighlightingRefinedBlocklistDescription[] =
     "Narrow the Blocklist for enabling Shared Highlighting.";
 
+const char kSharedHighlightingRefinedMaxContextWordsName[] =
+    "Shared Highlighting Max Context Words Refinement";
+const char kSharedHighlightingRefinedMaxContextWordsDescription[] =
+    "Experiment with different Max Context Words for Shared Highlighting.";
+
 const char kDraw1PredictedPoint12Ms[] = "1 point 12ms ahead.";
 const char kDraw2PredictedPoints6Ms[] = "2 points, each 6ms ahead.";
 const char kDraw1PredictedPoint6Ms[] = "1 point 6ms ahead.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 6a2f7b0..99908d28 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -319,12 +319,6 @@
 extern const char kCanvasOopRasterizationName[];
 extern const char kCanvasOopRasterizationDescription[];
 
-extern const char kCertificateTransparency2022PolicyName[];
-extern const char kCertificateTransparency2022PolicyDescription[];
-
-extern const char kCertificateTransparency2022PolicyAllCertsName[];
-extern const char kCertificateTransparency2022PolicyAllCertsDescription[];
-
 extern const char kCheckOfflineCapabilityName[];
 extern const char kCheckOfflineCapabilityDescription[];
 
@@ -1640,6 +1634,9 @@
 extern const char kSharedHighlightingRefinedBlocklistName[];
 extern const char kSharedHighlightingRefinedBlocklistDescription[];
 
+extern const char kSharedHighlightingRefinedMaxContextWordsName[];
+extern const char kSharedHighlightingRefinedMaxContextWordsDescription[];
+
 extern const char kDraw1PredictedPoint12Ms[];
 extern const char kDraw2PredictedPoints6Ms[];
 extern const char kDraw1PredictedPoint6Ms[];
diff --git a/chrome/browser/mac/chrome_browser_main_extra_parts_mac.h b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.h
new file mode 100644
index 0000000..6f2faad
--- /dev/null
+++ b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MAC_CHROME_BROWSER_MAIN_EXTRA_PARTS_MAC_H_
+#define CHROME_BROWSER_MAC_CHROME_BROWSER_MAIN_EXTRA_PARTS_MAC_H_
+
+#include <memory>
+
+#include "chrome/browser/chrome_browser_main_extra_parts.h"
+
+namespace display {
+class ScopedNativeScreen;
+}
+
+class ChromeBrowserMainExtraPartsMac : public ChromeBrowserMainExtraParts {
+ public:
+  ChromeBrowserMainExtraPartsMac();
+  ChromeBrowserMainExtraPartsMac(const ChromeBrowserMainExtraPartsMac&) =
+      delete;
+  ChromeBrowserMainExtraPartsMac& operator=(
+      const ChromeBrowserMainExtraPartsMac&) = delete;
+  ~ChromeBrowserMainExtraPartsMac() override;
+
+  // ChromeBrowserMainExtraParts:
+  void PreEarlyInitialization() override;
+
+ private:
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
+};
+
+#endif  // CHROME_BROWSER_MAC_CHROME_BROWSER_MAIN_EXTRA_PARTS_MAC_H_
diff --git a/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm
new file mode 100644
index 0000000..3094e1f
--- /dev/null
+++ b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm
@@ -0,0 +1,14 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/mac/chrome_browser_main_extra_parts_mac.h"
+
+#include "ui/display/screen.h"
+
+ChromeBrowserMainExtraPartsMac::ChromeBrowserMainExtraPartsMac() = default;
+ChromeBrowserMainExtraPartsMac::~ChromeBrowserMainExtraPartsMac() = default;
+
+void ChromeBrowserMainExtraPartsMac::PreEarlyInitialization() {
+  screen_ = std::make_unique<display::ScopedNativeScreen>();
+}
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc
index 310c743..dcb59b0 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc
@@ -40,17 +40,16 @@
       delete;
 
   void SetUp() override {
+    display::Screen::SetScreenInstance(&screen_);
     ChromeRenderViewHostTestHarness::SetUp();
-    previous_screen_ = display::Screen::SetScreenInstance(&screen_);
     tab_usage_scenario_tracker_ =
         std::make_unique<TabUsageScenarioTracker>(&usage_scenario_data_store_);
   }
 
   void TearDown() override {
     tab_usage_scenario_tracker_.reset();
-    display::Screen::SetScreenInstance(previous_screen_);
-    previous_screen_ = nullptr;
     ChromeRenderViewHostTestHarness::TearDown();
+    display::Screen::SetScreenInstance(nullptr);
   }
 
   std::unique_ptr<content::WebContents> CreateWebContents() {
@@ -83,7 +82,6 @@
 
  protected:
   display::test::TestScreen screen_;
-  raw_ptr<display::Screen> previous_screen_;
   UsageScenarioDataStoreImpl usage_scenario_data_store_;
   std::unique_ptr<TabUsageScenarioTracker> tab_usage_scenario_tracker_;
   ukm::TestAutoSetUkmRecorder ukm_recorder_;
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.cc b/chrome/browser/policy/messaging_layer/public/report_client.cc
index b26d082e..28c7ddd 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client.cc
@@ -77,8 +77,7 @@
 class ReportingClient::Uploader : public UploaderInterface {
  public:
   using UploadCallback =
-      base::OnceCallback<Status(bool,
-                                std::unique_ptr<std::vector<EncryptedRecord>>)>;
+      base::OnceCallback<Status(bool, std::vector<EncryptedRecord>)>;
 
   static std::unique_ptr<Uploader> Create(bool need_encryption_key,
                                           UploadCallback upload_callback) {
@@ -123,7 +122,7 @@
    private:
     bool completed_{false};
     const bool need_encryption_key_;
-    std::unique_ptr<std::vector<EncryptedRecord>> encrypted_records_;
+    std::vector<EncryptedRecord> encrypted_records_;
 
     UploadCallback upload_callback_;
   };
@@ -140,7 +139,6 @@
     bool need_encryption_key,
     ReportingClient::Uploader::UploadCallback upload_callback)
     : need_encryption_key_(need_encryption_key),
-      encrypted_records_(std::make_unique<std::vector<EncryptedRecord>>()),
       upload_callback_(std::move(upload_callback)) {}
 
 void ReportingClient::Uploader::Helper::ProcessRecord(
@@ -150,7 +148,7 @@
     std::move(processed_cb).Run(false);
     return;
   }
-  encrypted_records_->emplace_back(std::move(data));
+  encrypted_records_.emplace_back(std::move(data));
   std::move(processed_cb).Run(true);
 }
 
@@ -163,8 +161,8 @@
     return;
   }
   for (uint64_t i = 0; i < count; ++i) {
-    encrypted_records_->emplace_back();
-    *encrypted_records_->rbegin()->mutable_sequence_information() = start;
+    encrypted_records_.emplace_back();
+    *encrypted_records_.rbegin()->mutable_sequence_information() = start;
     start.set_sequencing_id(start.sequencing_id() + 1);
   }
   std::move(processed_cb).Run(true);
@@ -181,8 +179,7 @@
     return;
   }
   completed_ = true;
-  DCHECK(encrypted_records_);
-  if (encrypted_records_->empty() && !need_encryption_key_) {
+  if (encrypted_records_.empty() && !need_encryption_key_) {
     return;
   }
   DCHECK(upload_callback_);
@@ -344,7 +341,7 @@
                 base::BindOnce(
                     [](EncryptedReportingUploadProvider* upload_provider,
                        bool need_encryption_key,
-                       std::unique_ptr<std::vector<EncryptedRecord>> records) {
+                       std::vector<EncryptedRecord> records) {
                       upload_provider->RequestUploadEncryptedRecords(
                           need_encryption_key, std::move(records),
                           base::DoNothing());
diff --git a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
index 0a8e97d..9c739d9 100644
--- a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
+++ b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.cc
@@ -70,7 +70,7 @@
 
 DmServerUploader::DmServerUploader(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     RecordHandler* handler,
     ReportSuccessfulUploadCallback report_success_upload_cb,
     EncryptionKeyAttachedCallback encryption_key_attached_cb,
@@ -95,13 +95,13 @@
     return;
   }
   // Early exit if we don't have any records and do not need encryption key.
-  if (encrypted_records_->empty() && !need_encryption_key_) {
+  if (encrypted_records_.empty() && !need_encryption_key_) {
     Complete(
         Status(error::INVALID_ARGUMENT, "No records received for upload."));
     return;
   }
 
-  if (!encrypted_records_->empty()) {
+  if (!encrypted_records_.empty()) {
     ProcessRecords();
   }
 
@@ -113,13 +113,13 @@
   Status process_status;
 
   const int64_t expected_generation_id =
-      encrypted_records_->front().sequence_information().generation_id();
+      encrypted_records_.front().sequence_information().generation_id();
   int64_t expected_sequencing_id =
-      encrypted_records_->front().sequence_information().sequencing_id();
+      encrypted_records_.front().sequence_information().sequencing_id();
 
   // Will stop processing records on the first record that fails to pass.
   size_t records_added = 0;
-  for (const EncryptedRecord& encrypted_record : *encrypted_records_) {
+  for (const auto& encrypted_record : encrypted_records_) {
     process_status = IsRecordValid(encrypted_record, expected_generation_id,
                                    expected_sequencing_id);
     if (!process_status.ok()) {
@@ -136,7 +136,7 @@
   }
 
   // Discarding the remaining records.
-  encrypted_records_->resize(records_added);
+  encrypted_records_.resize(records_added);
 }
 
 void DmServerUploader::HandleRecords() {
@@ -205,7 +205,7 @@
 
 Status DmServerUploadService::EnqueueUpload(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     ReportSuccessfulUploadCallback report_upload_success_cb,
     EncryptionKeyAttachedCallback encryption_key_attached_cb) {
   Start<DmServerUploader>(need_encryption_key, std::move(records),
diff --git a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
index 3f181f3..0cbc02f 100644
--- a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
+++ b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service.h
@@ -80,7 +80,7 @@
     // Any errors will result in |upload_complete| being called with a Status.
     virtual void HandleRecords(
         bool need_encryption_key,
-        std::unique_ptr<std::vector<EncryptedRecord>> records,
+        std::vector<EncryptedRecord> records,
         DmServerUploadService::CompletionCallback upload_complete,
         DmServerUploadService::EncryptionKeyAttachedCallback
             encryption_key_attached_cb) = 0;
@@ -100,7 +100,7 @@
    public:
     DmServerUploader(
         bool need_encryption_key,
-        std::unique_ptr<std::vector<EncryptedRecord>> records,
+        std::vector<EncryptedRecord> records,
         RecordHandler* handler,
         ReportSuccessfulUploadCallback report_success_upload_cb,
         EncryptionKeyAttachedCallback encryption_key_attached_cb,
@@ -135,7 +135,7 @@
                          const int64_t expected_sequencing_id) const;
 
     const bool need_encryption_key_;
-    std::unique_ptr<std::vector<EncryptedRecord>> encrypted_records_;
+    std::vector<EncryptedRecord> encrypted_records_;
     const ReportSuccessfulUploadCallback report_success_upload_cb_;
     const EncryptionKeyAttachedCallback encryption_key_attached_cb_;
     raw_ptr<RecordHandler> handler_;
@@ -161,7 +161,7 @@
 
   Status EnqueueUpload(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records,
+      std::vector<EncryptedRecord> records,
       ReportSuccessfulUploadCallback report_upload_success_cb,
       EncryptionKeyAttachedCallback encryption_key_attached_cb);
 
diff --git a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc
index 120ebff9..847880e 100644
--- a/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/dm_server_upload_service_unittest.cc
@@ -51,7 +51,7 @@
   ~TestRecordHandler() override = default;
 
   void HandleRecords(bool need_encryption_key,
-                     std::unique_ptr<std::vector<EncryptedRecord>> records,
+                     std::vector<EncryptedRecord> records,
                      DmServerUploadService::CompletionCallback upload_complete,
                      DmServerUploadService::EncryptionKeyAttachedCallback
                          encryption_key_attached_cb) override {
@@ -62,7 +62,7 @@
   MOCK_METHOD(void,
               HandleRecords_,
               (bool,
-               std::unique_ptr<std::vector<EncryptedRecord>>&,
+               std::vector<EncryptedRecord>&,
                DmServerUploadService::CompletionCallback,
                DmServerUploadService::EncryptionKeyAttachedCallback));
 };
@@ -71,8 +71,7 @@
  public:
   DmServerTest()
       : sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner({})),
-        handler_(std::make_unique<TestRecordHandler>()),
-        records_(std::make_unique<std::vector<EncryptedRecord>>()) {}
+        handler_(std::make_unique<TestRecordHandler>()) {}
 
  protected:
   content::BrowserTaskEnvironment task_envrionment_{
@@ -81,7 +80,7 @@
   scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
 
   std::unique_ptr<TestRecordHandler> handler_;
-  std::unique_ptr<std::vector<EncryptedRecord>> records_;
+  std::vector<EncryptedRecord> records_;
 
   const base::TimeDelta kMaxDelay_ = base::Seconds(1);
 };
@@ -105,7 +104,7 @@
 
 TEST_P(DmServerUploaderTest, ProcessesRecord) {
   // Add an empty record.
-  records_->emplace_back();
+  records_.emplace_back();
 
   const bool force_confirm_flag = force_confirm();
   EXPECT_CALL(*handler_, HandleRecords_(_, _, _, _))
@@ -157,7 +156,7 @@
     sequence_information->set_generation_id(kGenerationId);
     sequence_information->set_sequencing_id(i);
     sequence_information->set_priority(Priority::IMMEDIATE);
-    records_->push_back(std::move(encrypted_record));
+    records_.push_back(std::move(encrypted_record));
   }
 
   const bool force_confirm_flag = force_confirm();
@@ -199,7 +198,7 @@
 
 TEST_P(DmServerUploaderTest, ReportsFailureToProcess) {
   // Add an empty record.
-  records_->emplace_back();
+  records_.emplace_back();
 
   EXPECT_CALL(*handler_, HandleRecords_(_, _, _, _))
       .WillOnce(WithArgs<2>(
@@ -281,7 +280,7 @@
 TEST_P(DmServerFailureTest, ReportsFailureToUpload) {
   const error::Code& error_code = GetParam();
   // Add an empty record.
-  records_->emplace_back();
+  records_.emplace_back();
 
   EXPECT_CALL(*handler_, HandleRecords_(_, _, _, _))
       .WillOnce(WithArgs<2>(Invoke(
diff --git a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
index 7907579..9fdada4f 100644
--- a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
+++ b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.cc
@@ -93,11 +93,11 @@
 
 Status FakeUploadClient::EnqueueUpload(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     ReportSuccessfulUploadCallback report_upload_success_cb,
     EncryptionKeyAttachedCallback encryption_key_attached_cb) {
   UploadEncryptedReportingRequestBuilder builder;
-  for (auto record : *records) {
+  for (auto record : records) {
     builder.AddRecord((std::move(record)));
   }
   auto request_result = builder.Build();
diff --git a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.h b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.h
index 0baaf615..885ae03 100644
--- a/chrome/browser/policy/messaging_layer/upload/fake_upload_client.h
+++ b/chrome/browser/policy/messaging_layer/upload/fake_upload_client.h
@@ -22,7 +22,7 @@
 
   Status EnqueueUpload(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records,
+      std::vector<EncryptedRecord> records,
       ReportSuccessfulUploadCallback report_upload_success_cb,
       EncryptionKeyAttachedCallback encryption_key_attached_cb) override;
 
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
index b190950..1b4ca5a4 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
@@ -72,7 +72,7 @@
  public:
   ReportUploader(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records,
+      std::vector<EncryptedRecord> records,
       policy::CloudPolicyClient* client,
       DmServerUploadService::CompletionCallback upload_complete_cb,
       DmServerUploadService::EncryptionKeyAttachedCallback
@@ -107,7 +107,7 @@
       const base::Value::Dict& value);
 
   bool need_encryption_key_;
-  std::unique_ptr<std::vector<EncryptedRecord>> records_;
+  std::vector<EncryptedRecord> records_;
   raw_ptr<policy::CloudPolicyClient> client_;
 
   // Encryption key delivery callback.
@@ -133,7 +133,7 @@
 
 RecordHandlerImpl::ReportUploader::ReportUploader(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     policy::CloudPolicyClient* client,
     DmServerUploadService::CompletionCallback client_cb,
     DmServerUploadService::EncryptionKeyAttachedCallback
@@ -157,14 +157,7 @@
     return;
   }
 
-  if (records_ == nullptr) {
-    Status null_records = Status(error::INVALID_ARGUMENT, "records_ was null");
-    LOG(ERROR) << null_records;
-    Complete(null_records);
-    return;
-  }
-
-  if (records_->empty() && !need_encryption_key_) {
+  if (records_.empty() && !need_encryption_key_) {
     Status empty_records =
         Status(error::INVALID_ARGUMENT, "records_ was empty");
     LOG(ERROR) << empty_records;
@@ -181,8 +174,8 @@
                      base::Unretained(this));
 
   UploadEncryptedReportingRequestBuilder request_builder{need_encryption_key_};
-  for (auto record : *records_) {
-    request_builder.AddRecord((std::move(record)));
+  for (auto record : records_) {
+    request_builder.AddRecord(std::move(record));
   }
 
   // Assign random UUID as the request id for server side log correlation
@@ -196,7 +189,7 @@
   }
 
   // Records have been captured in the request, safe to clear the vector.
-  records_->clear();
+  records_.clear();
 
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE,
@@ -313,11 +306,11 @@
     if (gap_record_result.has_value()) {
       LOG(ERROR) << "Data Loss. Record was unprocessable by the server: "
                  << *failed_uploaded_record;
-      records_->push_back(std::move(gap_record_result.value()));
+      records_.push_back(std::move(gap_record_result.value()));
     }
   }
 
-  if (!records_->empty()) {
+  if (!records_.empty()) {
     // Upload the next record but do not request encryption key again.
     StartUpload();
     return;
@@ -431,7 +424,7 @@
 
 void RecordHandlerImpl::HandleRecords(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     DmServerUploadService::CompletionCallback upload_complete_cb,
     DmServerUploadService::EncryptionKeyAttachedCallback
         encryption_key_attached_cb) {
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.h b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.h
index c6bc2ab..6cd8d27 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.h
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.h
@@ -33,7 +33,7 @@
 
   // Base class RecordHandler method implementation.
   void HandleRecords(bool need_encryption_key,
-                     std::unique_ptr<std::vector<EncryptedRecord>> record,
+                     std::vector<EncryptedRecord> record,
                      DmServerUploadService::CompletionCallback upload_complete,
                      DmServerUploadService::EncryptionKeyAttachedCallback
                          encryption_key_attached_cb) override;
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
index 6863044..be14080e 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
@@ -93,12 +93,11 @@
   std::unique_ptr<policy::MockCloudPolicyClient> client_;
 };
 
-std::unique_ptr<std::vector<EncryptedRecord>> BuildTestRecordsVector(
+std::vector<EncryptedRecord> BuildTestRecordsVector(
     int64_t number_of_test_records,
     int64_t generation_id) {
-  std::unique_ptr<std::vector<EncryptedRecord>> test_records =
-      std::make_unique<std::vector<EncryptedRecord>>();
-  test_records->reserve(number_of_test_records);
+  std::vector<EncryptedRecord> test_records;
+  test_records.reserve(number_of_test_records);
   for (int64_t i = 0; i < number_of_test_records; i++) {
     EncryptedRecord encrypted_record;
     encrypted_record.set_encrypted_wrapped_record(
@@ -108,7 +107,7 @@
     sequence_information->set_generation_id(generation_id);
     sequence_information->set_sequencing_id(i);
     sequence_information->set_priority(Priority::IMMEDIATE);
-    test_records->push_back(std::move(encrypted_record));
+    test_records.push_back(std::move(encrypted_record));
   }
   return test_records;
 }
@@ -120,7 +119,7 @@
   const auto force_confirm_by_server = force_confirm();
 
   DmServerUploadService::SuccessfulUploadResponse expected_response{
-      .sequence_information = test_records->back().sequence_information(),
+      .sequence_information = test_records.back().sequence_information(),
       .force_confirm = force_confirm()};
 
   EXPECT_CALL(*client_, UploadEncryptedReport(IsDataUploadRequestValid(), _, _))
@@ -217,7 +216,7 @@
   static constexpr int64_t kGenerationId = 1234;
   // test records that has one record with missing sequence information.
   auto test_records = BuildTestRecordsVector(kNumTestRecords, kGenerationId);
-  test_records->back().clear_sequence_information();
+  test_records.back().clear_sequence_information();
 
   // The response should show an error and UploadEncryptedReport should not have
   // been even called, because UploadEncryptedReportingRequestBuilder::Build()
@@ -272,7 +271,7 @@
 
   const DmServerUploadService::SuccessfulUploadResponse expected_response{
       .sequence_information =
-          (*test_records)[kNumTestRecords - 1].sequence_information(),
+          test_records[kNumTestRecords - 1].sequence_information(),
       .force_confirm = force_confirm()};
 
   // Once for failure, and once for gap.
@@ -350,7 +349,7 @@
   const auto force_confirm_by_server = force_confirm();
 
   DmServerUploadService::SuccessfulUploadResponse expected_response{
-      .sequence_information = test_records->back().sequence_information(),
+      .sequence_information = test_records.back().sequence_information(),
       .force_confirm = force_confirm()};
 
   EXPECT_CALL(*client_, UploadEncryptedReport(IsDataUploadRequestValid(), _, _))
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client.cc b/chrome/browser/policy/messaging_layer/upload/upload_client.cc
index 7bee165..d03bc09 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client.cc
@@ -40,12 +40,10 @@
 
 Status UploadClient::EnqueueUpload(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     ReportSuccessfulUploadCallback report_upload_success_cb,
     EncryptionKeyAttachedCallback encryption_key_attached_cb) {
-  DCHECK(records);
-
-  if (records->empty() && !need_encryption_key) {
+  if (records.empty() && !need_encryption_key) {
     return Status::StatusOK();
   }
 
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client.h b/chrome/browser/policy/messaging_layer/upload/upload_client.h
index db94c491..41f52b3 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client.h
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client.h
@@ -45,7 +45,7 @@
 
   virtual Status EnqueueUpload(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> record,
+      std::vector<EncryptedRecord> record,
       ReportSuccessfulUploadCallback report_upload_success_cb,
       EncryptionKeyAttachedCallback encryption_key_attached_cb);
 
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
index 3c05fb43..c8ff8cd3 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
@@ -124,18 +124,16 @@
 
   std::string serialized_record;
   wrapped_record.SerializeToString(&serialized_record);
-  std::unique_ptr<std::vector<EncryptedRecord>> records =
-      std::make_unique<std::vector<EncryptedRecord>>();
+  std::vector<EncryptedRecord> records;
   for (int64_t i = 0; i < kExpectedCallTimes; i++) {
     EncryptedRecord encrypted_record;
     encrypted_record.set_encrypted_wrapped_record(serialized_record);
-
     SequenceInformation* sequence_information =
         encrypted_record.mutable_sequence_information();
     sequence_information->set_sequencing_id(static_cast<int64_t>(i));
     sequence_information->set_generation_id(kGenerationId);
     sequence_information->set_priority(Priority::IMMEDIATE);
-    records->push_back(encrypted_record);
+    records.push_back(encrypted_record);
   }
 
   StrictMock<TestEncryptionKeyAttached> encryption_key_attached;
@@ -196,7 +194,7 @@
 
   // Save last record seq info for verification.
   const SequenceInformation last_record_seq_info =
-      records->back().sequence_information();
+      records.back().sequence_information();
 
   test::TestEvent<StatusOr<std::unique_ptr<UploadClient>>> e;
   UploadClient::Create(client.get(), e.cb());
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_provider.cc b/chrome/browser/policy/messaging_layer/upload/upload_provider.cc
index 498336e8..b2282007 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_provider.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_provider.cc
@@ -43,7 +43,7 @@
 
   // Uploads encrypted records (can be invoked on any thread).
   void EnqueueUpload(bool need_encryption_key,
-                     std::unique_ptr<std::vector<EncryptedRecord>> records,
+                     std::vector<EncryptedRecord> records,
                      base::OnceCallback<void(Status)> enqueued_cb) const;
 
  private:
@@ -65,10 +65,9 @@
   // Uploads encrypted records on sequenced task runner (and thus capable of
   // detecting whether upload client is ready or not). If not ready,
   // it will wait and then upload.
-  void EnqueueUploadInternal(
-      bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records,
-      base::OnceCallback<void(Status)> enqueued_cb);
+  void EnqueueUploadInternal(bool need_encryption_key,
+                             std::vector<EncryptedRecord> records,
+                             base::OnceCallback<void(Status)> enqueued_cb);
 
   // Sequence task runner and checker used during
   // |PostNewCloudPolicyClientRequest| processing.
@@ -97,8 +96,7 @@
   // because it did not yet get a confirmation from server), we will only
   // hold to one set of records.
   // Guarded by sequenced_task_runner_.
-  base::flat_map</*generation_id*/ int64_t,
-                 std::unique_ptr<std::vector<EncryptedRecord>>>
+  base::flat_map</*generation_id*/ int64_t, std::vector<EncryptedRecord>>
       stored_records_;
   bool stored_need_encryption_key_{false};
 
@@ -211,7 +209,7 @@
   upload_client_request_in_progress_ = false;
 
   // Upload client is ready, upload all previously stored requests (if any).
-  auto records = std::make_unique<std::vector<EncryptedRecord>>();
+  std::vector<EncryptedRecord> records;
   while (!stored_records_.empty() || stored_need_encryption_key_) {
     if (!stored_records_.empty()) {
       records = std::move(stored_records_.begin()->second);
@@ -230,7 +228,7 @@
 
 void EncryptedReportingUploadProvider::UploadHelper::EnqueueUpload(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     base::OnceCallback<void(Status)> enqueued_cb) const {
   sequenced_task_runner_->PostTask(
       FROM_HERE,
@@ -241,16 +239,15 @@
 
 void EncryptedReportingUploadProvider::UploadHelper::EnqueueUploadInternal(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     base::OnceCallback<void(Status)> enqueued_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequenced_task_checker_);
   if (upload_client_ == nullptr) {
     stored_need_encryption_key_ |= need_encryption_key;
     int64_t generation_id = 0;
-    if (records && !records->empty() &&
-        records->begin()->has_sequence_information() &&
-        records->begin()->sequence_information().has_generation_id()) {
-      generation_id = records->begin()->sequence_information().generation_id();
+    if (!records.empty() && records.begin()->has_sequence_information() &&
+        records.begin()->sequence_information().has_generation_id()) {
+      generation_id = records.begin()->sequence_information().generation_id();
     }
     stored_records_.emplace(generation_id, std::move(records));
     // Report success even though the upload has not been executed.
@@ -285,7 +282,7 @@
 
 void EncryptedReportingUploadProvider::RequestUploadEncryptedRecords(
     bool need_encryption_key,
-    std::unique_ptr<std::vector<EncryptedRecord>> records,
+    std::vector<EncryptedRecord> records,
     base::OnceCallback<void(Status)> result_cb) {
   DCHECK(helper_);
   helper_->EnqueueUpload(need_encryption_key, std::move(records),
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_provider.h b/chrome/browser/policy/messaging_layer/upload/upload_provider.h
index 07d11bb7..28b63a2 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_provider.h
+++ b/chrome/browser/policy/messaging_layer/upload/upload_provider.h
@@ -49,7 +49,7 @@
   // Called to upload records and/or request encryption key.
   void RequestUploadEncryptedRecords(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records,
+      std::vector<EncryptedRecord> records,
       base::OnceCallback<void(Status)> result_cb);
 
  private:
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_provider_unittest.cc b/chrome/browser/policy/messaging_layer/upload/upload_provider_unittest.cc
index b16fe2e..0825a56 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_provider_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_provider_unittest.cc
@@ -108,7 +108,7 @@
 
   Status CallRequestUploadEncryptedRecord(
       bool need_encryption_key,
-      std::unique_ptr<std::vector<EncryptedRecord>> records) {
+      std::vector<EncryptedRecord> records) {
     test::TestEvent<Status> result;
     service_provider_->RequestUploadEncryptedRecords(
         need_encryption_key, std::move(records), result.cb());
@@ -134,8 +134,8 @@
               UploadEncryptedReport(IsDataUploadRequestValid(), _, _))
       .WillOnce(MakeUploadEncryptedReportAction());
 
-  auto records = std::make_unique<std::vector<EncryptedRecord>>();
-  records->push_back(record_);
+  std::vector<EncryptedRecord> records;
+  records.push_back(record_);
   const auto status = CallRequestUploadEncryptedRecord(
       /*need_encryption_key=*/false, std::move(records));
   EXPECT_OK(status) << status;
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 2a2563e..5a2f92a 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -122,7 +122,6 @@
       "components:closure_compile",
       "domain_reliability_internals:closure_compile",
       "engagement:closure_compile",
-      "memory_internals:closure_compile",
     ]
     if (is_linux || is_chromeos || is_win || is_mac) {
       deps += [
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
index 1249795a..13cb8e6a 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.html
@@ -106,7 +106,7 @@
         [[i18nDynamic(locale, 'consolidatedConsentGoogleEulaTitle')]]
       </h1>
       <div slot="content" class="flex layout vertical">
-        <webview id="googleEulaWebview" role="document"
+        <webview id="consolidatedConsentGoogleEulaWebview" role="document"
             class="oobe-tos-webview tos-webview"
             on-contentload="onGoogleEulaContentLoad_">
         </webview>
@@ -128,7 +128,7 @@
         [[i18nDynamic(locale, 'consolidatedConsentCrosEulaTitle')]]
       </h1>
       <div slot="content" class="flex layout vertical">
-        <webview id="crosEulaWebview" role="document"
+        <webview id="consolidatedConsentCrosEulaWebview" role="document"
             class="oobe-tos-webview tos-webview"
             on-contentload="onCrosEulaContentLoad_">
         </webview>
@@ -149,7 +149,7 @@
         [[i18nDynamic(locale, 'consolidatedConsentArcTermsTitle')]]
       </h1>
       <div slot="content" class="flex layout vertical">
-        <webview id="arcTosWebview" role="document"
+        <webview id="consolidatedConsentArcTosWebview" role="document"
             class="oobe-tos-webview tos-webview"
             on-contentload="onArcTosContentLoad_">
         </webview>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
index 8a57aaf..05c5b2b 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
@@ -256,7 +256,7 @@
     }
 
     this.isArcTosInitialized_ = true;
-    const webview = this.$.arcTosWebview;
+    const webview = this.$.consolidatedConsentArcTosWebview;
     webview.removeContentScripts(['preProcess']);
 
     var language = this.getCurrentLanguage_();
@@ -299,10 +299,11 @@
       this.googleEulaLoading_ = true;
       this.crosEulaLoading_ = true;
       this.loadEulaWebview_(
-          this.$.googleEulaWebview, this.googleEulaUrl_,
+          this.$.consolidatedConsentGoogleEulaWebview, this.googleEulaUrl_,
           false /* clear_anchors */);
       this.loadEulaWebview_(
-          this.$.crosEulaWebview, this.crosEulaUrl_, true /* clear_anchors */);
+          this.$.consolidatedConsentCrosEulaWebview, this.crosEulaUrl_,
+          true /* clear_anchors */);
     }
 
     if (this.shouldShowArcTos_(isTosHidden, isArcEnabled)) {
@@ -326,7 +327,7 @@
   }
 
   loadArcTosWebview_(online_tos_url) {
-    const webview = this.$.arcTosWebview;
+    const webview = this.$.consolidatedConsentArcTosWebview;
 
     var loadFailureCallback = () => {
       this.setUIStep(ConsolidatedConsentScreenState.ERROR);
@@ -406,7 +407,7 @@
   }
 
   onArcTosContentLoad_() {
-    const webview = this.$.arcTosWebview;
+    const webview = this.$.consolidatedConsentArcTosWebview;
     webview.executeScript({code: 'getPrivacyPolicyLink();'}, (results) => {
       if (results && results.length == 1 && typeof results[0] == 'string') {
         this.loadPrivacyPolicyWebview_(results[0]);
@@ -721,7 +722,7 @@
 
     // Enable loading content script 'playstore.js' when fetching ToS from
     // the test server.
-    var termsView = this.$.arcTosWebview;
+    var termsView = this.$.consolidatedConsentArcTosWebview;
     termsView.removeContentScripts(['postProcess']);
     termsView.addContentScripts([{
       name: 'postProcess',
diff --git a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.html b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.html
index c387679..71bf83fd 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.html
@@ -59,7 +59,7 @@
         [[i18nDynamic(locale, 'guestTosGoogleEulaTitle')]]
       </h1>
       <div slot="content" class="flex layout vertical">
-        <webview id="googleEulaWebview" role="document"
+        <webview id="guestTosGoogleEulaWebview" role="document"
             class="oobe-tos-webview tos-webview"
             on-contentload="onGoogleEulaContentLoad_">
         </webview>
@@ -80,7 +80,7 @@
         [[i18nDynamic(locale, 'guestTosCrosEulaTitle')]]
       </h1>
       <div slot="content" class="flex layout vertical">
-        <webview id="crosEulaWebview" role="document"
+        <webview id="guestTosCrosEulaWebview" role="document"
             class="oobe-tos-webview tos-webview">
         </webview>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
index e4453d4..9260099a 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
@@ -85,9 +85,10 @@
     const crosEulaUrl = data['crosEulaUrl'];
 
     this.loadEulaWebview_(
-        this.$.googleEulaWebview, googleEulaUrl, false /* clear_anchors */);
+        this.$.guestTosGoogleEulaWebview, googleEulaUrl,
+        false /* clear_anchors */);
     this.loadEulaWebview_(
-        this.$.crosEulaWebview, crosEulaUrl, true /* clear_anchors */);
+        this.$.guestTosCrosEulaWebview, crosEulaUrl, true /* clear_anchors */);
   }
 
   /** Initial UI State for screen */
diff --git a/chrome/browser/resources/history/app.ts b/chrome/browser/resources/history/app.ts
index ec71ede0..c52b96ec 100644
--- a/chrome/browser/resources/history/app.ts
+++ b/chrome/browser/resources/history/app.ts
@@ -19,6 +19,7 @@
 import {CrDrawerElement} from 'chrome://resources/cr_elements/cr_drawer/cr_drawer.js';
 import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 import {FindShortcutMixin, FindShortcutMixinInterface} from 'chrome://resources/cr_elements/find_shortcut_mixin.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {hasKeyModifiers} from 'chrome://resources/js/util.m.js';
@@ -241,6 +242,7 @@
   private tabsIcons_: Array<string>;
   private tabsNames_: Array<string>;
   private toolbarShadow_: boolean;
+  private historyClustersViewStartTime_: Date|null = null;
 
   constructor() {
     super();
@@ -259,6 +261,8 @@
 
     this.eventTracker_.add(
         document, 'keydown', e => this.onKeyDown_(e as KeyboardEvent));
+    this.eventTracker_.add(
+        document, 'visibilitychange', this.onVisibilityChange_.bind(this));
     this.addWebUIListener(
         'sign-in-state-changed',
         (signedIn: boolean) => this.onSignInStateChanged_(signedIn));
@@ -404,6 +408,21 @@
     }
   }
 
+  private onVisibilityChange_() {
+    if (this.selectedPage_ !== Page.HISTORY_CLUSTERS) {
+      return;
+    }
+
+    if (document.visibilityState === 'hidden') {
+      this.recordHistoryClustersDuration_();
+    } else if (
+        document.visibilityState === 'visible' &&
+        this.historyClustersViewStartTime_ === null) {
+      // Restart the timer if the user switches back to the History tab.
+      this.historyClustersViewStartTime_ = new Date();
+    }
+  }
+
   private onDeleteCommand_() {
     if (this.$.toolbar.count === 0 || this.pendingDelete_) {
       return;
@@ -460,10 +479,18 @@
     return querying && !incremental && searchTerm !== '';
   }
 
-  private selectedPageChanged_() {
+  private selectedPageChanged_(newPage: Page, oldPage: Page) {
     this.unselectAll();
     this.historyViewChanged_();
     this.maybeUpdateSelectedHistoryTab_();
+
+    if (oldPage === Page.HISTORY_CLUSTERS &&
+        newPage !== Page.HISTORY_CLUSTERS) {
+      this.recordHistoryClustersDuration_();
+    }
+    if (newPage === Page.HISTORY_CLUSTERS) {
+      this.historyClustersViewStartTime_ = new Date();
+    }
   }
 
   private updateScrollTarget_() {
@@ -507,6 +534,18 @@
     this.recordHistoryPageView_();
   }
 
+  // Records the history clusters page duration.
+  private recordHistoryClustersDuration_() {
+    assert(this.historyClustersViewStartTime_ !== null);
+
+    const duration =
+        new Date().getTime() - this.historyClustersViewStartTime_.getTime();
+    this.browserService_!.recordLongTime(
+        'History.Clusters.WebUISessionDuration', duration);
+
+    this.historyClustersViewStartTime_ = null;
+  }
+
   private hasDrawerChanged_() {
     const drawer = this.$.drawer.getIfExists();
     if (!this.hasDrawer_ && drawer && drawer.open) {
diff --git a/chrome/browser/resources/history/browser_service.ts b/chrome/browser/resources/history/browser_service.ts
index 73990400..ff10c52f 100644
--- a/chrome/browser/resources/history/browser_service.ts
+++ b/chrome/browser/resources/history/browser_service.ts
@@ -33,6 +33,7 @@
   recordHistogram(histogram: string, value: number, max: number): void;
   recordAction(action: string): void;
   recordTime(histogram: string, time: number): void;
+  recordLongTime(histogram: string, time: number): void;
   navigateToUrl(url: string, target: string, e: MouseEvent): void;
   otherDevicesInitialized(): void;
   queryHistoryContinuation(): Promise<QueryResult>;
@@ -96,6 +97,13 @@
     chrome.send('metricsHandler:recordTime', [histogram, time]);
   }
 
+  recordLongTime(histogram: string, time: number) {
+    // It's a bit odd that this is the only one to use chrome.metricsPrivate,
+    // but that's because the other code predates chrome.metricsPrivate.
+    // In any case, the MetricsHandler doesn't support long time histograms.
+    chrome.metricsPrivate.recordLongTime(histogram, time);
+  }
+
   navigateToUrl(url: string, target: string, e: MouseEvent) {
     chrome.send(
         'navigateToUrl',
diff --git a/chrome/browser/resources/memory_internals/BUILD.gn b/chrome/browser/resources/memory_internals/BUILD.gn
index 6ab9dbc..0918d56 100644
--- a/chrome/browser/resources/memory_internals/BUILD.gn
+++ b/chrome/browser/resources/memory_internals/BUILD.gn
@@ -2,15 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/typescript/ts_library.gni")
 
-js_type_check("closure_compile") {
-  deps = [ ":memory_internals" ]
-}
-
-js_library("memory_internals") {
-  deps = [
-    "//ui/webui/resources/js:cr.m",
-    "//ui/webui/resources/js:util.m",
-  ]
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  in_files = [ "memory_internals.ts" ]
+  definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  deps = [ "//ui/webui/resources:library" ]
 }
diff --git a/chrome/browser/resources/memory_internals/memory_internals.js b/chrome/browser/resources/memory_internals/memory_internals.ts
similarity index 85%
rename from chrome/browser/resources/memory_internals/memory_internals.js
rename to chrome/browser/resources/memory_internals/memory_internals.ts
index c10e06c..89993984 100644
--- a/chrome/browser/resources/memory_internals/memory_internals.js
+++ b/chrome/browser/resources/memory_internals/memory_internals.ts
@@ -5,6 +5,13 @@
 import {addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {$} from 'chrome://resources/js/util.m.js';
 
+type Process = [number, string, boolean];
+
+type ProcessList = {
+  message: string,
+  processes: Process[],
+};
+
 function requestProcessList() {
   sendWithPromise('requestProcessList').then(onProcessListReceived);
 }
@@ -13,17 +20,18 @@
   chrome.send('saveDump');
 }
 
-function reportProcess(pid) {
+function reportProcess(pid: number) {
   chrome.send('reportProcess', [pid]);
 }
 
-function startProfiling(pid) {
+function startProfiling(pid: number) {
   chrome.send('startProfiling', [pid]);
 }
 
 // celltype should either be "td" or "th". The contents of the |cols| will be
 // added as children of each table cell if they are non-null.
-function addListRow(table, celltype, cols) {
+function addListRow(
+    table: HTMLElement, celltype: string, cols: Array<Text|HTMLElement|null>) {
   const tr = document.createElement('tr');
   for (const col of cols) {
     const cell = document.createElement(celltype);
@@ -35,7 +43,7 @@
   table.appendChild(tr);
 }
 
-function onProcessListReceived(data) {
+function onProcessListReceived(data: ProcessList) {
   $('message').innerText = data['message'];
 
   const proclist = $('proclist');
@@ -80,7 +88,7 @@
   $('refresh').onclick = requestProcessList;
   $('save').onclick = saveDump;
 
-  addWebUIListener('save-dump-progress', progress => {
+  addWebUIListener('save-dump-progress', (progress: string) => {
     $('save-dump-text').innerText = progress;
   });
 
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
index c743998..3b5b440 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_page.html
@@ -9,14 +9,23 @@
       <div class="start">
         $i18n{crostiniPageLabel}
         <div class="secondary" id="secondaryText">
-          <localized-link
-              localized-string="[[i18nAdvanced('crostiniSubtext')]]">
-          </localized-link>
+          <template is="dom-if" if="[[showCrostini]]" restamp>
+            <localized-link
+                localized-string="[[i18nAdvanced('crostiniSubtext')]]">
+            </localized-link>
+          </template>
+          <template is="dom-if" if="[[!showCrostini]]" restamp>
+            <localized-link localized-string="[[i18nAdvanced(
+                'crostiniSubtextNotSupported')]]">
+            </localized-link>
+          </template>
         </div>
       </div>
-      <template is="dom-if" if="[[!allowCrostini]]" restamp>
-        <cr-policy-indicator indicator-type="userPolicy">
-        </cr-policy-indicator>
+      <template is="dom-if" if="[[showCrostini]]" restamp>
+        <template is="dom-if" if="[[!allowCrostini]]" restamp>
+          <cr-policy-indicator indicator-type="userPolicy">
+          </cr-policy-indicator>
+        </template>
       </template>
       <template is="dom-if" if="[[prefs.crostini.enabled.value]]">
         <cr-icon-button class="subpage-arrow"
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 19c1c71..867108f0 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -218,10 +218,10 @@
   }
 
   // Crostini section.
+  r.CROSTINI =
+      createSection(r.ADVANCED, mojom.CROSTINI_SECTION_PATH, Section.kCrostini);
   if (loadTimeData.valueExists('showCrostini') &&
       loadTimeData.getBoolean('showCrostini')) {
-    r.CROSTINI = createSection(
-        r.ADVANCED, mojom.CROSTINI_SECTION_PATH, Section.kCrostini);
     r.CROSTINI_DETAILS = createSubpage(
         r.CROSTINI, mojom.CROSTINI_DETAILS_SUBPAGE_PATH,
         Subpage.kCrostiniDetails);
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
index 892fff5..2499a9e 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -214,7 +214,7 @@
         <iron-icon icon="os-settings:print"></iron-icon>
         $i18n{printingPageTitle}
       </a>
-      <a href="/crostini" hidden="[[!showCrostini]]" class="item">
+      <a href="/crostini" class="item">
         <iron-icon icon="os-settings:developer-tags"></iron-icon>
         $i18n{crostiniPageTitle}
       </a>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 7fba828e..f7bb5d7 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -241,14 +241,13 @@
         <os-settings-printing-page prefs="{{prefs}}">
         </os-settings-printing-page>
       </settings-section>
-      <template is="dom-if" if="[[showCrostini]]" restamp>
-        <settings-section page-title="$i18n{crostiniPageTitle}"
-            section="crostini">
-          <settings-crostini-page prefs="{{prefs}}"
-              allow-crostini="[[allowCrostini_]]">
-          </settings-crostini-page>
-        </settings-section>
-      </template>
+      <settings-section page-title="$i18n{crostiniPageTitle}"
+          section="crostini">
+        <settings-crostini-page prefs="{{prefs}}"
+            allow-crostini="[[allowCrostini_]]"
+            show-crostini="[[showCrostini]]">
+        </settings-crostini-page>
+      </settings-section>
       <template is="dom-if" if="[[!isAccessibilityOSSettingsVisibilityEnabled_]]">
         <settings-section page-title="$i18n{a11yPageTitle}"
             section="osAccessibility">
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
index fb17b913..981f73fd 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
@@ -120,7 +120,6 @@
       <template is="dom-if" id="drawerTemplate">
         <os-settings-menu
             show-apps="[[showApps_]]"
-            show-crostini="[[showCrostini_]]"
             show-plugin-vm="[[showPluginVm_]]"
             show-reset="[[showReset_]]"
             show-startup="[[showStartup_]]"
@@ -137,7 +136,6 @@
   <div id="left">
     <template is="dom-if" if="[[showNavMenu_]]">
       <os-settings-menu page-visibility="[[pageVisibility_]]"
-          show-crostini="[[showCrostini_]]"
           show-reset="[[showReset_]]"
           show-startup="[[showStartup_]]"
           have-play-store-app="[[havePlayStoreApp_]]"
diff --git a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
index f8b8fd2..83026a3 100644
--- a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
+++ b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android.cc
@@ -4,11 +4,13 @@
 
 #include "chrome/browser/segmentation_platform/default_model/chrome_start_model_android.h"
 
+#include <array>
+
 #include "base/metrics/field_trial_params.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
 #include "chrome/browser/ui/android/start_surface/start_surface_android.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/public/model_provider.h"
 
@@ -36,61 +38,24 @@
 // InputFeatures.
 constexpr int32_t kProfileSigninStatusEnums[] = {0 /* All profiles syncing */,
                                                  2 /* Mixed sync status */};
-constexpr MetadataWriter::UMAFeature kChromeStartUMAFeatures[] = {
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.Open",
-        .bucket_count = 7,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab",
-        .bucket_count = 7,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.OpenInNewTab",
-        .bucket_count = 7,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileNTPMostVisited",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileNewTabOpened",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileMenuRecentTabs",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileMenuHistory",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::HISTOGRAM_ENUM,
-                               .name = "UMA.ProfileSignInStatus",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 2,
-                               .accepted_enum_ids = kProfileSigninStatusEnums}};
-
-#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))
+constexpr std::array<MetadataWriter::UMAFeature, 8> kChromeStartUMAFeatures = {
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.Open",
+        7),
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab",
+        7),
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.OpenInNewTab",
+        7),
+    MetadataWriter::UMAFeature::FromUserAction("MobileNTPMostVisited", 7),
+    MetadataWriter::UMAFeature::FromUserAction("MobileNewTabOpened", 7),
+    MetadataWriter::UMAFeature::FromUserAction("MobileMenuRecentTabs", 7),
+    MetadataWriter::UMAFeature::FromUserAction("MobileMenuHistory", 7),
+    MetadataWriter::UMAFeature::FromEnumHistogram("UMA.ProfileSignInStatus",
+                                                  7,
+                                                  kProfileSigninStatusEnums,
+                                                  2)};
 
 }  // namespace
 
@@ -111,8 +76,8 @@
                                    kDiscreteMappings, 1);
 
   // Set features.
-  writer.AddUmaFeatures(kChromeStartUMAFeatures,
-                        ARRAY_SIZE(kChromeStartUMAFeatures));
+  writer.AddUmaFeatures(kChromeStartUMAFeatures.data(),
+                        kChromeStartUMAFeatures.size());
 
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
@@ -124,7 +89,7 @@
 void ChromeStartModel::ExecuteModelWithInput(const std::vector<float>& inputs,
                                              ExecutionCallback callback) {
   // Invalid inputs.
-  if (inputs.size() != ARRAY_SIZE(kChromeStartUMAFeatures)) {
+  if (inputs.size() != kChromeStartUMAFeatures.size()) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
     return;
diff --git a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
index c3d62af..edd98512 100644
--- a/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/chrome_start_model_android_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
diff --git a/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc b/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
index 69f5a9b..bdb4f4ca 100644
--- a/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
+++ b/chrome/browser/segmentation_platform/default_model/feed_user_segment.cc
@@ -4,10 +4,12 @@
 
 #include "chrome/browser/segmentation_platform/default_model/feed_user_segment.h"
 
+#include <array>
+
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/strcat.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/model_provider.h"
@@ -47,66 +49,23 @@
     }};
 
 // InputFeatures.
-constexpr MetadataWriter::UMAFeature kFeedUserUMAFeatures[] = {
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.Open",
-        .bucket_count = 14,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab",
-        .bucket_count = 14,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{
-        .signal_type = proto::SignalType::USER_ACTION,
-        .name = "ContentSuggestions.Feed.CardAction.OpenInNewTab",
-        .bucket_count = 14,
-        .tensor_length = 1,
-        .aggregation = proto::Aggregation::COUNT,
-        .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileNTPMostVisited",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileNewTabOpened",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "Home",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileMenuRecentTabs",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileMenuHistory",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileTabReturnedToCurrentTab",
-                               .bucket_count = 14,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0}};
-
-#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))
+constexpr std::array<MetadataWriter::UMAFeature, 9> kFeedUserUMAFeatures = {
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.Open",
+        14),
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab",
+        14),
+    MetadataWriter::UMAFeature::FromUserAction(
+        "ContentSuggestions.Feed.CardAction.OpenInNewTab",
+        14),
+    MetadataWriter::UMAFeature::FromUserAction("MobileNTPMostVisited", 14),
+    MetadataWriter::UMAFeature::FromUserAction("MobileNewTabOpened", 14),
+    MetadataWriter::UMAFeature::FromUserAction("Home", 14),
+    MetadataWriter::UMAFeature::FromUserAction("MobileMenuRecentTabs", 14),
+    MetadataWriter::UMAFeature::FromUserAction("MobileMenuHistory", 14),
+    MetadataWriter::UMAFeature::FromUserAction("MobileTabReturnedToCurrentTab",
+                                               14)};
 
 float GetScoreForSubsegment(FeedUserSubsegment subgroup) {
   for (const auto& score_and_type : kFeedUserScoreToSubGroup) {
@@ -174,7 +133,8 @@
       kFeedUserScoreToSubGroup.data(), kFeedUserScoreToSubGroup.size());
 
   // Set features.
-  writer.AddUmaFeatures(kFeedUserUMAFeatures, ARRAY_SIZE(kFeedUserUMAFeatures));
+  writer.AddUmaFeatures(kFeedUserUMAFeatures.data(),
+                        kFeedUserUMAFeatures.size());
 
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
@@ -186,7 +146,7 @@
 void FeedUserSegment::ExecuteModelWithInput(const std::vector<float>& inputs,
                                             ExecutionCallback callback) {
   // Invalid inputs.
-  if (inputs.size() != ARRAY_SIZE(kFeedUserUMAFeatures)) {
+  if (inputs.size() != kFeedUserUMAFeatures.size()) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
     return;
diff --git a/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc b/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
index 71ea016..af0e807 100644
--- a/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/feed_user_segment_unittest.cc
@@ -6,44 +6,10 @@
 
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
-namespace {
-
-// TODO(ssid): Use metadata_utils or share common code for this function.
-int ConvertToDiscreteScore(const std::string& mapping_key,
-                           float input_score,
-                           const proto::SegmentationModelMetadata& metadata) {
-  auto iter = metadata.discrete_mappings().find(mapping_key);
-  if (iter == metadata.discrete_mappings().end()) {
-    iter =
-        metadata.discrete_mappings().find(metadata.default_discrete_mapping());
-    if (iter == metadata.discrete_mappings().end())
-      return 0;
-  }
-  DCHECK(iter != metadata.discrete_mappings().end());
-
-  const auto& mapping = iter->second;
-
-  // Iterate over the entries and find the largest entry whose min result is
-  // equal to or less than the input.
-  int discrete_result = 0;
-  float largest_score_below_input_score = std::numeric_limits<float>::min();
-  for (int i = 0; i < mapping.entries_size(); i++) {
-    const auto& entry = mapping.entries(i);
-    if (entry.min_result() <= input_score &&
-        entry.min_result() > largest_score_below_input_score) {
-      largest_score_below_input_score = entry.min_result();
-      discrete_result = entry.rank();
-    }
-  }
-
-  return discrete_result;
-}
-
-}  // namespace
 
 class FeedUserModelTest : public testing::Test {
  public:
@@ -117,32 +83,36 @@
 
   absl::optional<float> result = ExpectExecutionWithInput(input);
   ASSERT_TRUE(result);
-  EXPECT_EQ("NoNTPOrHomeOpened",
-            FeedUserSegment::GetSubsegmentName(ConvertToDiscreteScore(
-                "feed_user_segment_subsegment", *result, *fetched_metadata_)));
+  EXPECT_EQ(
+      "NoNTPOrHomeOpened",
+      FeedUserSegment::GetSubsegmentName(metadata_utils::ConvertToDiscreteScore(
+          "feed_user_segment_subsegment", *result, *fetched_metadata_)));
 
   input[4] = 3;
   input[5] = 2;
   result = ExpectExecutionWithInput(input);
   ASSERT_TRUE(result);
-  EXPECT_EQ("UsedNtpWithoutModules",
-            FeedUserSegment::GetSubsegmentName(ConvertToDiscreteScore(
-                "feed_user_segment_subsegment", *result, *fetched_metadata_)));
+  EXPECT_EQ(
+      "UsedNtpWithoutModules",
+      FeedUserSegment::GetSubsegmentName(metadata_utils::ConvertToDiscreteScore(
+          "feed_user_segment_subsegment", *result, *fetched_metadata_)));
 
   input[3] = 3;
   result = ExpectExecutionWithInput(input);
   ASSERT_TRUE(result);
-  EXPECT_EQ("MvtOnly",
-            FeedUserSegment::GetSubsegmentName(ConvertToDiscreteScore(
-                "feed_user_segment_subsegment", *result, *fetched_metadata_)));
+  EXPECT_EQ(
+      "MvtOnly",
+      FeedUserSegment::GetSubsegmentName(metadata_utils::ConvertToDiscreteScore(
+          "feed_user_segment_subsegment", *result, *fetched_metadata_)));
 
   input[0] = 1;
   input[2] = 2;
   result = ExpectExecutionWithInput(input);
   ASSERT_TRUE(result);
-  EXPECT_EQ("ActiveOnFeedAndNtpFeatures",
-            FeedUserSegment::GetSubsegmentName(ConvertToDiscreteScore(
-                "feed_user_segment_subsegment", *result, *fetched_metadata_)));
+  EXPECT_EQ(
+      "ActiveOnFeedAndNtpFeatures",
+      FeedUserSegment::GetSubsegmentName(metadata_utils::ConvertToDiscreteScore(
+          "feed_user_segment_subsegment", *result, *fetched_metadata_)));
 
   EXPECT_FALSE(ExpectExecutionWithInput({}));
   EXPECT_FALSE(ExpectExecutionWithInput({1, 2}));
diff --git a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
index 626ad6b..69e6eab9 100644
--- a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
+++ b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model.cc
@@ -4,10 +4,12 @@
 
 #include "chrome/browser/segmentation_platform/default_model/low_user_engagement_model.h"
 
+#include <array>
+
 #include "base/logging.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
-#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 
 namespace segmentation_platform {
 
@@ -33,7 +35,7 @@
     {kChromeStartDiscreteMappingMinResult, kChromeStartDiscreteMappingRank}};
 
 // InputFeatures.
-constexpr MetadataWriter::UMAFeature kChromeStartUMAFeatures[] = {
+constexpr std::array<MetadataWriter::UMAFeature, 1> kChromeStartUMAFeatures = {
     MetadataWriter::UMAFeature{
         .signal_type = proto::SignalType::HISTOGRAM_VALUE,
         .name = "Session.TotalDuration",
@@ -42,8 +44,6 @@
         .aggregation = proto::Aggregation::BUCKETED_COUNT,
         .enum_ids_size = 0}};
 
-#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))
-
 }  // namespace
 
 LowUserEngagementModel::LowUserEngagementModel()
@@ -63,8 +63,8 @@
                                    kDiscreteMappings, 1);
 
   // Set features.
-  writer.AddUmaFeatures(kChromeStartUMAFeatures,
-                        ARRAY_SIZE(kChromeStartUMAFeatures));
+  writer.AddUmaFeatures(kChromeStartUMAFeatures.data(),
+                        kChromeStartUMAFeatures.size());
 
   constexpr int kModelVersion = 1;
   base::SequencedTaskRunnerHandle::Get()->PostTask(
diff --git a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
index e9b1f16..dbd9a6f1 100644
--- a/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/low_user_engagement_model_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
diff --git a/chrome/browser/segmentation_platform/default_model/metadata_writer.h b/chrome/browser/segmentation_platform/default_model/metadata_writer.h
deleted file mode 100644
index 953699d..0000000
--- a/chrome/browser/segmentation_platform/default_model/metadata_writer.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_METADATA_WRITER_H_
-#define CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_METADATA_WRITER_H_
-
-#include <cinttypes>
-#include <cstddef>
-
-#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
-
-namespace segmentation_platform {
-
-// TODO(ssid): Move these functions to components/segmentation to be used by all
-// the default models.
-
-// Utility to write metadata proto for default models.
-class MetadataWriter {
- public:
-  explicit MetadataWriter(proto::SegmentationModelMetadata* metadata);
-  ~MetadataWriter();
-
-  MetadataWriter(MetadataWriter&) = delete;
-  MetadataWriter& operator=(MetadataWriter&) = delete;
-
-  // Defines a feature based on UMA metric.
-  struct UMAFeature {
-    const proto::SignalType signal_type{proto::SignalType::UNKNOWN_SIGNAL_TYPE};
-    const char* name{nullptr};
-    const uint64_t bucket_count{0};
-    const uint64_t tensor_length{0};
-    const proto::Aggregation aggregation{proto::Aggregation::UNKNOWN};
-    const size_t enum_ids_size{0};
-    const int32_t* const accepted_enum_ids = nullptr;
-  };
-
-  // Appends list of UMA features in order.
-  void AddUmaFeatures(const UMAFeature features[], size_t features_size);
-
-  // Appends a list of discrete mapping in order.
-  void AddDiscreteMappingEntries(const std::string& key,
-                                 const std::pair<float, int>* mappings,
-                                 size_t mappings_size);
-
-  // Writes the model metadata with the given parameters.
-  void SetSegmentationMetadataConfig(proto::TimeUnit time_unit,
-                                     uint64_t bucket_duration,
-                                     int64_t signal_storage_length,
-                                     int64_t min_signal_collection_length,
-                                     int64_t result_time_to_live);
-
- private:
-  proto::SegmentationModelMetadata* const metadata_;
-};
-
-}  // namespace segmentation_platform
-
-#endif  // CHROME_BROWSER_SEGMENTATION_PLATFORM_DEFAULT_MODEL_METADATA_WRITER_H_
diff --git a/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc b/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
index 307819b..f63f6d7 100644
--- a/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
+++ b/chrome/browser/segmentation_platform/default_model/query_tiles_model.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/segmentation_platform/default_model/query_tiles_model.h"
 
+#include <array>
+
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/public/model_provider.h"
 
@@ -32,19 +34,11 @@
     {kQueryTilesDiscreteMappingMinResult, kQueryTilesDiscreteMappingRank}};
 
 // InputFeatures.
-constexpr MetadataWriter::UMAFeature kQueryTilesUMAFeatures[2] = {
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "MobileNTPMostVisited",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0},
-    MetadataWriter::UMAFeature{.signal_type = proto::SignalType::USER_ACTION,
-                               .name = "Search.QueryTiles.NTP.Tile.Clicked",
-                               .bucket_count = 7,
-                               .tensor_length = 1,
-                               .aggregation = proto::Aggregation::COUNT,
-                               .enum_ids_size = 0}};
+constexpr std::array<MetadataWriter::UMAFeature, 2> kQueryTilesUMAFeatures = {
+    MetadataWriter::UMAFeature::FromUserAction("MobileNTPMostVisited", 7),
+    MetadataWriter::UMAFeature::FromUserAction(
+        "Search.QueryTiles.NTP.Tile.Clicked",
+        7)};
 
 }  // namespace
 
@@ -65,9 +59,8 @@
                                    kDiscreteMappings, 1);
 
   // Set features.
-  writer.AddUmaFeatures(
-      kQueryTilesUMAFeatures,
-      sizeof(kQueryTilesUMAFeatures) / sizeof(kQueryTilesUMAFeatures[0]));
+  writer.AddUmaFeatures(kQueryTilesUMAFeatures.data(),
+                        kQueryTilesUMAFeatures.size());
 
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
@@ -78,7 +71,7 @@
 void QueryTilesModel::ExecuteModelWithInput(const std::vector<float>& inputs,
                                             ExecutionCallback callback) {
   // Invalid inputs.
-  if (inputs.size() != 2) {
+  if (inputs.size() != kQueryTilesUMAFeatures.size()) {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
     return;
diff --git a/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc b/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
index 82da0b7..82949d74 100644
--- a/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
+++ b/chrome/browser/segmentation_platform/default_model/query_tiles_model_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace segmentation_platform {
diff --git a/chrome/browser/storage/shared_storage_browsertest.cc b/chrome/browser/storage/shared_storage_browsertest.cc
index 982a5390..d61f9ef4 100644
--- a/chrome/browser/storage/shared_storage_browsertest.cc
+++ b/chrome/browser/storage/shared_storage_browsertest.cc
@@ -57,8 +57,8 @@
 }
 
 std::string GetSharedStorageDisabledErrorMessage() {
-  return base::StrCat({"a JavaScript error:\nError: ",
-                       content::GetSharedStorageDisabledMessage(), "\n"});
+  return base::StrCat({"a JavaScript error: \"Error: ",
+                       content::GetSharedStorageDisabledMessage(), "\"\n"});
 }
 
 }  // namespace
@@ -260,7 +260,7 @@
 
   if (!SuccessExpected()) {
     // Shared Storage will be disabled.
-    EXPECT_EQ("a JavaScript error:\nError: sharedStorage is disabled\n",
+    EXPECT_EQ("a JavaScript error: \"Error: sharedStorage is disabled\"\n",
               result.error);
     EXPECT_EQ(0u, console_observer.messages().size());
     return;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5546366..65a57c7 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -4195,6 +4195,8 @@
       "views/collected_cookies_views.h",
       "views/commander_frontend_views.cc",
       "views/commander_frontend_views.h",
+      "views/commerce/ntp_discount_consent_dialog_view.cc",
+      "views/commerce/ntp_discount_consent_dialog_view.h",
       "views/confirm_bubble_views.cc",
       "views/confirm_bubble_views.h",
       "views/constrained_web_dialog_delegate_views.cc",
@@ -4943,6 +4945,8 @@
       "views/web_apps/pwa_confirmation_bubble_view.h",
       "views/web_apps/web_app_confirmation_view.cc",
       "views/web_apps/web_app_confirmation_view.h",
+      "views/web_apps/web_app_detailed_install_dialog.cc",
+      "views/web_apps/web_app_detailed_install_dialog.h",
       "views/web_apps/web_app_identity_update_confirmation_view.cc",
       "views/web_apps/web_app_identity_update_confirmation_view.h",
       "views/web_apps/web_app_info_image_source.cc",
@@ -5581,6 +5585,7 @@
     "//components/permissions",
     "//components/sessions",
     "//components/translate/content/browser",
+    "//components/url_formatter",
     "//components/zoom",
     "//content/public/browser",
     "//content/public/common",
diff --git a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
index a1e1c70..6d4e185 100644
--- a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
+++ b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java
@@ -93,6 +93,11 @@
     }
 
     /**
+     * Notify the native page that it is about to be navigated back or hidden by a back press.
+     */
+    default void notifyHidingWithBack() {}
+
+    /**
      * Called after a page has been removed from the view hierarchy and will no longer be used.
      */
     void destroy();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
index 16758e6..8509664 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.Tracker;
@@ -90,6 +91,11 @@
 
         Tab tab = mTabSupplier.get();
         if (tab != null && tab.canGoBack()) {
+            NativePage nativePage = tab.getNativePage();
+            if (nativePage != null) {
+                nativePage.notifyHidingWithBack();
+            }
+
             tab.goBack();
             mOnSuccessRunnable.run();
             return true;
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 3dcc97c..abf12dd 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -78,7 +78,8 @@
   virtual void SetAppStatus(const std::string& id, ash::AppStatus app_status) {}
   virtual void SetItemPosition(const std::string& id,
                                const syncer::StringOrdinal& new_position) {}
-  virtual void SetItemIsPersistent(const std::string& id, bool is_persistent) {}
+  virtual void SetItemIsSystemFolder(const std::string& id,
+                                     bool is_system_folder) {}
   virtual void SetIsNewInstall(const std::string& id, bool is_new_install) {}
   virtual void SetItemFolderId(const std::string& id,
                                const std::string& folder_id) = 0;
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 1ac9eae..6b709cc 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -245,7 +245,7 @@
     const AppListSyncableService::SyncItem& folder_item) {
   if (folder_item.item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
     return false;
-  return folder_item.is_persistent_folder;
+  return folder_item.is_system_folder;
 }
 
 // Updates `target` if `target` is different from a valid new value. Returns
@@ -583,7 +583,7 @@
   auto iter = sync_items_.find(id);
   if (iter != sync_items_.end())
     return iter->second.get();
-  return NULL;
+  return nullptr;
 }
 
 void AppListSyncableService::AppListSyncableService::AddPageBreakItem(
@@ -808,7 +808,7 @@
   const std::string& item_id = app_item->id();
   if (item_id.empty()) {
     LOG(ERROR) << "ChromeAppListItem item with empty ID";
-    return NULL;
+    return nullptr;
   }
   SyncItem* sync_item = FindSyncItem(item_id);
   if (sync_item) {
@@ -820,7 +820,7 @@
     }
 
     if (RemoveDefaultApp(app_item, sync_item))
-      return NULL;
+      return nullptr;
 
     // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
     // App entry can be added.
@@ -1777,9 +1777,9 @@
     changed = true;
   }
 
-  if (sync_item->is_persistent_folder != app_item->is_persistent()) {
-    DCHECK(!sync_item->is_persistent_folder);
-    sync_item->is_persistent_folder = app_item->is_persistent();
+  if (sync_item->is_system_folder != app_item->is_system_folder()) {
+    DCHECK(!sync_item->is_system_folder);
+    sync_item->is_system_folder = app_item->is_system_folder();
     // Do not mark the item as changed - the persistent value is not expected to
     // be persisted to local state, nor synced. Also, it's expected to be set as
     // part of folder item creation flow, so no further processing should be
@@ -1829,7 +1829,7 @@
   auto oem_folder = std::make_unique<ChromeAppListItem>(profile_, folder_id,
                                                         model_updater_.get());
   oem_folder->SetChromeName(oem_folder_name_);
-  oem_folder->SetIsPersistent(true);
+  oem_folder->SetIsSystemFolder(true);
   oem_folder->SetChromeIsFolder(true);
 
   SyncItem* current_sync_data = FindSyncItem(folder_id);
@@ -1865,7 +1865,7 @@
                                     model_updater_.get());
   crostini_folder.SetChromeName(
       l10n_util::GetStringUTF8(IDS_APP_LIST_CROSTINI_DEFAULT_FOLDER_NAME));
-  crostini_folder.SetIsPersistent(true);
+  crostini_folder.SetIsSystemFolder(true);
   crostini_folder.SetChromeIsFolder(true);
 
   // Calculate the Crostini folder's position.
@@ -1913,7 +1913,7 @@
   new_folder_item->SetMetadata(
       app_list::GenerateItemMetadataFromSyncItem(*folder_sync_item));
   if (IsSystemCreatedSyncFolder(*folder_sync_item))
-    new_folder_item->SetIsPersistent(true);
+    new_folder_item->SetIsSystemFolder(true);
   model_updater_->AddItem(std::move(new_folder_item));
   return true;
 }
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h
index b80d3140..33e085c 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.h
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.h
@@ -70,12 +70,11 @@
     syncer::StringOrdinal item_pin_ordinal;
     ash::IconColor item_color;
 
-    // Indicates whether the item represents a persistent folder - i.e. a folder
-    // that was not created explicitly by a user, and which should not be
-    // removed if it's left with a single child.
+    // Indicates whether the item represents a system-created folder - i.e. a
+    // folder that was not created explicitly by a user.
     // Unlike other properties, this value is not persisted to local state, nor
     // synced. It reflects the associated ChromeAppListItem state.
-    bool is_persistent_folder = false;
+    bool is_system_folder = false;
 
     // Whether the `item_ordinal` should be fixed after initial sync data is
     // received during a user session.
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
index bb2c59b..6aee17ee 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -115,8 +115,8 @@
 }
 
 // Matches a chrome app item if its persistence field is set to true.
-MATCHER(IsPersistentApp, "") {
-  return arg->is_persistent();
+MATCHER(IsSystemFolder, "") {
+  return arg->is_system_folder();
 }
 
 // Get a set of all apps in |model|.
@@ -921,7 +921,7 @@
                   IsChromeApp(_, kDummyApp2Name, ash::kCrostiniFolderId),
                   testing::AllOf(
                       IsChromeApp(ash::kCrostiniFolderId, kRootFolderName, ""),
-                      IsPersistentApp())));
+                      IsSystemFolder())));
 }
 
 // Test that the Terminal app is removed when Crostini is disabled.
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 951db40..0d09e3e 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -209,11 +209,11 @@
   metadata_->position = position;
 }
 
-void ChromeAppListItem::SetIsPersistent(bool is_persistent) {
-  metadata_->is_persistent = is_persistent;
+void ChromeAppListItem::SetIsSystemFolder(bool is_system_folder) {
+  metadata_->is_system_folder = is_system_folder;
   AppListModelUpdater* updater = model_updater();
   if (updater)
-    updater->SetItemIsPersistent(id(), is_persistent);
+    updater->SetItemIsSystemFolder(id(), is_system_folder);
 }
 
 void ChromeAppListItem::SetIsPageBreak(bool is_page_break) {
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 38c9f7f..0c0ce3a 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -63,7 +63,7 @@
   const std::string& name() const { return metadata_->name; }
   ash::AppStatus app_status() const { return metadata_->app_status; }
   bool is_folder() const { return metadata_->is_folder; }
-  bool is_persistent() const { return metadata_->is_persistent; }
+  bool is_system_folder() const { return metadata_->is_system_folder; }
   const gfx::ImageSkia& icon() const { return metadata_->icon; }
   const ash::IconColor& icon_color() const { return metadata_->icon_color; }
   bool is_page_break() const { return metadata_->is_page_break; }
@@ -83,7 +83,7 @@
   void SetAppStatus(ash::AppStatus app_status);
   void SetFolderId(const std::string& folder_id);
   void SetIsPageBreak(bool is_page_break);
-  void SetIsPersistent(bool is_persistent);
+  void SetIsSystemFolder(bool is_system_folder);
   void SetIsNewInstall(bool is_new_install);
 
   // The following methods won't make changes to Ash and it should be called
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 643fe80..f40a43ac 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
@@ -411,13 +411,13 @@
   model_.SetItemMetadata(id, std::move(data));
 }
 
-void ChromeAppListModelUpdater::SetItemIsPersistent(const std::string& id,
-                                                    bool is_persistent) {
+void ChromeAppListModelUpdater::SetItemIsSystemFolder(const std::string& id,
+                                                      bool is_system_folder) {
   ash::AppListItem* item = model_.FindItem(id);
   if (!item)
     return;
   std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
-  data->is_persistent = is_persistent;
+  data->is_system_folder = is_system_folder;
   model_.SetItemMetadata(id, std::move(data));
 }
 
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 c08b4a8..17ddcc0e 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
@@ -68,7 +68,8 @@
   void SetAppStatus(const std::string& id, ash::AppStatus app_status) override;
   void SetItemPosition(const std::string& id,
                        const syncer::StringOrdinal& new_position) override;
-  void SetItemIsPersistent(const std::string& id, bool is_persistent) override;
+  void SetItemIsSystemFolder(const std::string& id,
+                             bool is_system_folder) override;
   void SetIsNewInstall(const std::string& id, bool is_new_install) override;
   void SetItemFolderId(const std::string& id,
                        const std::string& folder_id) override;
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 0de2227..af39e4e4 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -228,6 +228,15 @@
     AppInstallationAcceptanceCallback callback,
     PwaInProductHelpState iph_state = PwaInProductHelpState::kNotShown);
 
+// Shows the Web App detailed install dialog.
+// The dialog shows app's detailed information including screenshots. Users then
+// confirm or cancel install in this dialog.
+void ShowWebAppDetailedInstallDialog(
+    content::WebContents* web_contents,
+    std::unique_ptr<WebAppInstallInfo> web_app_info,
+    AppInstallationAcceptanceCallback callback,
+    PwaInProductHelpState iph_state = PwaInProductHelpState::kNotShown);
+
 // Sets whether |ShowPWAInstallBubble| should accept immediately without any
 // user interaction.
 void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept);
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
index b3a5d1b..97aafaf 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -30,6 +30,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/switches.h"
@@ -677,12 +678,156 @@
   ASSERT_FALSE(IsWindowFullscreenForTabOrPending());
 }
 
+// Tests FullscreenController support for fullscreen capability delegation.
+// https://wicg.github.io/capability-delegation/spec.html
+// See related wpt/fullscreen/api/delegate-request.https.sub.tentative.html
+// TODO(crbug.com/1326575): Test opener->popup etc. messaging; add WPT coverage.
+class FullscreenCapabilityDelegationFullscreenControllerInteractiveTest
+    : public FullscreenControllerInteractiveTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  FullscreenCapabilityDelegationFullscreenControllerInteractiveTest() = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    FullscreenControllerInteractiveTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(GetParam()
+                                        ? switches::kEnableBlinkFeatures
+                                        : switches::kDisableBlinkFeatures,
+                                    "CapabilityDelegationFullscreenRequest");
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    content::SetupCrossSiteRedirector(embedded_test_server());
+    embedded_test_server()->ServeFilesFromSourceDirectory(
+        "chrome/test/data/capability_delegation");
+  }
+
+  // Returns a popup with `url`, opened via JS from `browser`'s active tab.
+  content::WebContents* OpenPopup(Browser* browser, const GURL& url) {
+    const std::string script = content::JsReplace(
+        "window.open($1, '', 'width=500,height=500');", url.spec());
+    content::ExecuteScriptAsync(
+        browser->tab_strip_model()->GetActiveWebContents(), script);
+    Browser* popup = ui_test_utils::WaitForBrowserToOpen();
+    EXPECT_NE(popup, browser);
+    content::WebContents* popup_contents =
+        popup->tab_strip_model()->GetActiveWebContents();
+    EXPECT_TRUE(WaitForRenderFrameReady(popup_contents->GetMainFrame()));
+    EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
+    return popup_contents;
+  }
+
+  // Run `script` on `initiator` with `options`, and compares `expected_result`
+  // with the script result and the `target` browser's fullscreen state.
+  void ExecScriptAndCheckFullscreen(content::WebContents* initiator,
+                                    Browser* target,
+                                    const std::string& script,
+                                    int options,
+                                    bool expected_result) {
+    FullscreenNotificationObserver fullscreen_observer(target);
+    EXPECT_EQ(expected_result, EvalJs(initiator, script, options));
+    if (expected_result)
+      fullscreen_observer.Wait();
+    EXPECT_EQ(expected_result, target->window()->IsFullscreen());
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(
+    FullscreenCapabilityDelegationFullscreenControllerInteractiveTest,
+    CapabilityDelegationSameOriginPopup) {
+  EXPECT_TRUE(embedded_test_server()->Start());
+
+  // Navigate to a page that requests fullscreen and replies on message receipt.
+  const GURL receiver_url = embedded_test_server()->GetURL(
+      "a.com", "/fullscreen_request_delegation_receiver.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), receiver_url));
+
+  // Open a same-origin popup that delegates fullscreen and reports the result.
+  const GURL initiator_url = embedded_test_server()->GetURL(
+      "a.com", "/fullscreen_request_delegation_initiator.html");
+  content::WebContents* popup = OpenPopup(browser(), initiator_url);
+
+  const std::string targetOrigin =
+      embedded_test_server()->GetOrigin("a.com").Serialize();
+  const std::string script_without_delegation = content::JsReplace(
+      "delegateCapability(window.opener, $1, '')", targetOrigin);
+  const std::string script_with_delegation = content::JsReplace(
+      "delegateCapability(window.opener, $1, 'fullscreen')", targetOrigin);
+
+  // Fullscreen is only granted with user activation and explicit delegation.
+  ExecScriptAndCheckFullscreen(popup, browser(), script_without_delegation,
+                               content::EXECUTE_SCRIPT_NO_USER_GESTURE, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_with_delegation,
+                               content::EXECUTE_SCRIPT_NO_USER_GESTURE, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_without_delegation,
+                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_with_delegation,
+                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
+                               GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(
+    FullscreenCapabilityDelegationFullscreenControllerInteractiveTest,
+    CapabilityDelegationCrossOriginPopup) {
+  EXPECT_TRUE(embedded_test_server()->Start());
+
+  // Navigate to a page that requests fullscreen and replies on message receipt.
+  const GURL receiver_url = embedded_test_server()->GetURL(
+      "a.com", "/fullscreen_request_delegation_receiver.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), receiver_url));
+
+  // Open a cross-origin popup that delegates fullscreen and reports the result.
+  const GURL initiator_url = embedded_test_server()->GetURL(
+      "b.com", "/fullscreen_request_delegation_initiator.html");
+  content::WebContents* popup = OpenPopup(browser(), initiator_url);
+
+  const std::string targetOrigin =
+      embedded_test_server()->GetOrigin("a.com").Serialize();
+  const std::string script_without_delegation = content::JsReplace(
+      "delegateCapability(window.opener, $1, '')", targetOrigin);
+  const std::string script_with_delegation = content::JsReplace(
+      "delegateCapability(window.opener, $1, 'fullscreen')", targetOrigin);
+
+  // Fullscreen is only granted with user activation and explicit delegation.
+  ExecScriptAndCheckFullscreen(popup, browser(), script_without_delegation,
+                               content::EXECUTE_SCRIPT_NO_USER_GESTURE, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_with_delegation,
+                               content::EXECUTE_SCRIPT_NO_USER_GESTURE, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_without_delegation,
+                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, false);
+  ExecScriptAndCheckFullscreen(popup, browser(), script_with_delegation,
+                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
+                               GetParam());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    FullscreenCapabilityDelegationFullscreenControllerInteractiveTest,
+    ::testing::Bool());
+
 // Tests FullscreenController support of Multi-Screen Window Placement features.
 // Sites with the Window Placement permission can request fullscreen on a
 // specific screen, move fullscreen windows to different displays, and more.
 class MultiScreenFullscreenControllerInteractiveTest
     : public FullscreenControllerInteractiveTest {
  public:
+  void SetUp() override {
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    screen_.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
+                                      display::DisplayList::Type::PRIMARY);
+    display::Screen::SetScreenInstance(&screen_);
+#endif
+    FullscreenControllerInteractiveTest::SetUp();
+  }
+
+  void TearDown() override {
+    FullscreenControllerInteractiveTest::TearDown();
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    display::Screen::SetScreenInstance(nullptr);
+#endif
+  }
+
   // Perform common setup operations for multi-screen fullscreen testing:
   // Mock a screen with two displays, move the browser onto the first display,
   // and auto-grant the Window Placement permission on its active tab.
@@ -692,12 +837,8 @@
     display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
         .UpdateDisplay("0+0-800x800,800+0-800x800");
 #else
-    original_screen_ = display::Screen::GetScreen();
-    screen_.display_list().AddDisplay({1, gfx::Rect(0, 0, 800, 800)},
-                                      display::DisplayList::Type::PRIMARY);
     screen_.display_list().AddDisplay({2, gfx::Rect(800, 0, 800, 800)},
                                       display::DisplayList::Type::NOT_PRIMARY);
-    display::Screen::SetScreenInstance(&screen_);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     EXPECT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
 
@@ -720,14 +861,6 @@
     return tab;
   }
 
-  void TearDown() override {
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-    if (original_screen_)
-      display::Screen::SetScreenInstance(original_screen_);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-    FullscreenControllerInteractiveTest::TearDown();
-  }
-
   // Wait for a JS content fullscreen change with the given script and options.
   void RequestContentFullscreenFromScript(
       const std::string& eval_js_script,
@@ -801,14 +934,12 @@
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   display::DisplayList& display_list() { return screen_.display_list(); }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-
  private:
   base::test::ScopedFeatureList feature_list_{
       blink::features::kWindowPlacement};
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  raw_ptr<display::Screen> original_screen_ = nullptr;
   display::ScreenBase screen_;
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif
 };
 
 // TODO(crbug.com/1034772): Disabled on Windows, where views::FullscreenHandler
diff --git a/chrome/browser/ui/views/DEPS b/chrome/browser/ui/views/DEPS
index 273916b..0bd5884 100644
--- a/chrome/browser/ui/views/DEPS
+++ b/chrome/browser/ui/views/DEPS
@@ -3,6 +3,7 @@
  "+components/fullscreen_control",
  "+components/live_caption",
  "+components/services/app_service/public",
+ "+components/url_formatter",
  "+components/user_education/views",
  "+third_party/libaddressinput",
  "+services/tracing/public",
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index e8413c7..90d0e06 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -1079,6 +1079,14 @@
   }
 }
 
+void BookmarkBarView::ChildPreferredSizeChanged(views::View* child) {
+  // only rerender
+  if (child != saved_tab_group_bar_)
+    return;
+
+  InvalidateDrop();
+}
+
 void BookmarkBarView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   node_data->role = ax::mojom::Role::kToolbar;
   node_data->SetName(l10n_util::GetStringUTF8(IDS_ACCNAME_BOOKMARKS));
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
index bfd86c5..58626c4 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
 #include "chrome/browser/ui/tabs/tab_group_theme.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h"
+#include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h"
 #include "components/bookmarks/browser/bookmark_model_observer.h"
 #include "components/bookmarks/browser/bookmark_node_data.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -171,7 +172,8 @@
   views::View::DropCallback GetDropCallback(
       const ui::DropTargetEvent& event) override;
   void OnThemeChanged() override;
-  void VisibilityChanged(View* starting_from, bool is_visible) override;
+  void VisibilityChanged(views::View* starting_from, bool is_visible) override;
+  void ChildPreferredSizeChanged(views::View* child) override;
 
   // AccessiblePaneView:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
@@ -319,7 +321,7 @@
 
   // Returns the node corresponding to |sender|, which is one of the
   // |bookmark_buttons_|.
-  const bookmarks::BookmarkNode* GetNodeForSender(View* sender) const;
+  const bookmarks::BookmarkNode* GetNodeForSender(views::View* sender) const;
 
   // Writes a BookmarkNodeData for node to data.
   void WriteBookmarkDragData(const bookmarks::BookmarkNode* node,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
index 1afacdb16..7e68b783 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_unittest.cc
@@ -22,6 +22,8 @@
 #include "chrome/browser/ui/app_list/app_list_util.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h"
 #include "chrome/browser/ui/views/native_widget_factory.h"
@@ -516,6 +518,38 @@
   EXPECT_TRUE(test_helper_->saved_tab_group_bar()->page_navigator());
 }
 
+TEST_F(BookmarkBarViewTest, OnSavedTabGroupUpdateBookmarkBarCallsLayout) {
+  SavedTabGroupKeyedService* keyed_service =
+      SavedTabGroupServiceFactory::GetForProfile(browser()->profile());
+  ASSERT_TRUE(keyed_service);
+  ASSERT_TRUE(keyed_service->model());
+
+  // Add 3 saved tab groups.
+  keyed_service->model()->Add(SavedTabGroup(
+      tab_groups::TabGroupId::GenerateNew(), std::u16string(u"tab group 1"),
+      tab_groups::TabGroupColorId::kGrey, {}));
+
+  tab_groups::TabGroupId button_2_id = tab_groups::TabGroupId::GenerateNew();
+  keyed_service->model()->Add(
+      SavedTabGroup(button_2_id, std::u16string(u"tab group 2"),
+                    tab_groups::TabGroupColorId::kGrey, {}));
+
+  keyed_service->model()->Add(SavedTabGroup(
+      tab_groups::TabGroupId::GenerateNew(), std::u16string(u"tab group 3"),
+      tab_groups::TabGroupColorId::kGrey, {}));
+
+  // Save the position of the 3rd button.
+  ASSERT_EQ(3u, test_helper_->saved_tab_group_bar()->children().size());
+  const auto* button_3 = test_helper_->saved_tab_group_bar()->children()[2];
+  gfx::Rect bounds_in_screen = button_3->GetBoundsInScreen();
+
+  // Remove the middle tab group.
+  keyed_service->model()->Remove(button_2_id);
+
+  // Make sure the positions of the buttons were updated.
+  EXPECT_EQ(bounds_in_screen, button_3->GetBoundsInScreen());
+}
+
 TEST_F(BookmarkBarViewInWidgetTest, UpdateTooltipText) {
   widget()->Show();
 
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index ae62eaa..c38304a 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -71,22 +71,26 @@
 void SavedTabGroupBar::SavedTabGroupAdded(const SavedTabGroup& group,
                                           int index) {
   AddTabGroupButton(group, index);
+  PreferredSizeChanged();
 }
 
 void SavedTabGroupBar::SavedTabGroupRemoved(int index) {
   RemoveTabGroupButton(index);
+  PreferredSizeChanged();
 }
 
 void SavedTabGroupBar::SavedTabGroupUpdated(const SavedTabGroup& group,
                                             int index) {
   RemoveTabGroupButton(index);
   AddTabGroupButton(group, index);
+  PreferredSizeChanged();
 }
 
 void SavedTabGroupBar::SavedTabGroupMoved(const SavedTabGroup& group,
                                           int old_index,
                                           int new_index) {
   ReorderChildView(children().at(old_index), new_index);
+  PreferredSizeChanged();
 }
 
 // TODO dpenning: Support the state of the SavedTabGroup open in a tab strip
diff --git a/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.cc b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.cc
new file mode 100644
index 0000000..ff358af
--- /dev/null
+++ b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.cc
@@ -0,0 +1,104 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.h"
+
+#include "base/callback_helpers.h"
+#include "chrome/browser/cart/chrome_cart.mojom.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
+
+#include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/chrome_typography.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace {
+// Spacing between child of the Discount Consent Dialog View
+constexpr int kChildSpacing = 24;
+}  // namespace
+
+// static
+void NtpDiscountConsentDialogView::Show(Browser* browser,
+                                        ActionCallback callback) {
+  constrained_window::CreateBrowserModalDialogViews(
+      std::make_unique<NtpDiscountConsentDialogView>(std::move(callback)),
+      browser->window()->GetNativeWindow())
+      ->Show();
+}
+
+NtpDiscountConsentDialogView::NtpDiscountConsentDialogView(
+    ActionCallback callback)
+    : callback_(std::move(callback)) {
+  // Set up dialog properties.
+  SetModalType(ui::MODAL_TYPE_WINDOW);
+  SetShowCloseButton(false);
+  SetOwnedByWidget(true);
+  // TODO(meiliang@): Set text for the button.
+  SetButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK);
+  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
+      DISTANCE_LARGE_MODAL_DIALOG_PREFERRED_WIDTH));
+
+  SetAcceptCallback(base::BindOnce(&NtpDiscountConsentDialogView::OnAccept,
+                                   base::Unretained(this)));
+  SetCancelCallback(base::BindOnce(&NtpDiscountConsentDialogView::OnReject,
+                                   base::Unretained(this)));
+  SetCloseCallback(base::BindOnce(&NtpDiscountConsentDialogView::OnDismiss,
+                                  base::Unretained(this)));
+
+  // Set up dialog content view.
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical,
+      ChromeLayoutProvider::Get()->GetInsetsMetric(views::INSETS_DIALOG),
+      kChildSpacing));
+
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+
+  AddChildView(std::make_unique<ThemeTrackingNonAccessibleImageView>(
+      *bundle.GetImageSkiaNamed(IDR_NTP_CART_DISCOUNT_CONSENT_LIGHT),
+      *bundle.GetImageSkiaNamed(IDR_NTP_CART_DISCOUNT_CONSENT_DARK),
+      base::BindRepeating(&NtpDiscountConsentDialogView::GetBackgroundColor,
+                          base::Unretained(this))));
+
+  auto title_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE),
+      CONTEXT_HEADLINE, views::style::STYLE_PRIMARY);
+  auto* title = AddChildView(std::move(title_label));
+  title->SetMultiLine(true);
+
+  auto consent_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY),
+      views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_SECONDARY);
+  auto* consent = AddChildView(std::move(consent_label));
+  consent->SetMultiLine(true);
+}
+
+NtpDiscountConsentDialogView::~NtpDiscountConsentDialogView() = default;
+
+SkColor NtpDiscountConsentDialogView::GetBackgroundColor() {
+  return GetWidget()->GetColorProvider()->GetColor(ui::kColorDialogBackground);
+}
+
+void NtpDiscountConsentDialogView::OnAccept() {
+  assert(callback_);
+  std::move(callback_).Run(chrome_cart::mojom::ConsentStatus::ACCEPTED);
+}
+
+void NtpDiscountConsentDialogView::OnReject() {
+  assert(callback_);
+  std::move(callback_).Run(chrome_cart::mojom::ConsentStatus::REJECTED);
+}
+
+void NtpDiscountConsentDialogView::OnDismiss() {
+  assert(callback_);
+  std::move(callback_).Run(chrome_cart::mojom::ConsentStatus::DISMISSED);
+}
diff --git a/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.h b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.h
new file mode 100644
index 0000000..a8cdd63
--- /dev/null
+++ b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_COMMERCE_NTP_DISCOUNT_CONSENT_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_COMMERCE_NTP_DISCOUNT_CONSENT_DIALOG_VIEW_H_
+
+#include "base/callback_helpers.h"
+#include "chrome/browser/cart/chrome_cart.mojom.h"
+#include "ui/views/window/dialog_delegate.h"
+
+class Browser;
+
+class NtpDiscountConsentDialogView : public views::DialogDelegateView {
+ public:
+  using ActionCallback =
+      base::OnceCallback<void(chrome_cart::mojom::ConsentStatus)>;
+  static void Show(Browser* browser, ActionCallback callback);
+  explicit NtpDiscountConsentDialogView(ActionCallback callback);
+  ~NtpDiscountConsentDialogView() override;
+
+ private:
+  ActionCallback callback_;
+
+  SkColor GetBackgroundColor();
+  // Called when the accept button is clicked.
+  void OnAccept();
+  // Called when the reject button is clicked.
+  void OnReject();
+  // Called when the Esc key is used.
+  void OnDismiss();
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_COMMERCE_NTP_DISCOUNT_CONSENT_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view_browsertest.cc b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view_browsertest.cc
new file mode 100644
index 0000000..aff3424c
--- /dev/null
+++ b/chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view_browsertest.cc
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/commerce/ntp_discount_consent_dialog_view.h"
+
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "content/public/test/browser_test.h"
+
+// Test harness for integration tests using NtpDiscountConsentView.
+class NtpDiscountConsentDialogViewBrowserTest : public DialogBrowserTest {
+ public:
+  NtpDiscountConsentDialogViewBrowserTest() = default;
+
+  NtpDiscountConsentDialogViewBrowserTest(
+      const NtpDiscountConsentDialogViewBrowserTest&) = delete;
+  NtpDiscountConsentDialogViewBrowserTest& operator=(
+      const NtpDiscountConsentDialogViewBrowserTest&) = delete;
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    NtpDiscountConsentDialogView::Show(
+        browser(),
+        base::BindOnce([](chrome_cart::mojom::ConsentStatus status) {}));
+  }
+};
+
+// Shows the dialog for bookmarking all tabs. This shows a BookmarkEditorView
+// dialog, with a tree view, where a user can rename and select a parent folder.
+IN_PROC_BROWSER_TEST_F(NtpDiscountConsentDialogViewBrowserTest,
+                       InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index 9c53c60..d06b4aa3dcb 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -60,8 +60,6 @@
          key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_LEFT;
 }
 
-}  // namespace
-
 // IntentPickerLabelButton
 
 // A button that represents a candidate intent handler.
@@ -103,6 +101,131 @@
 BEGIN_METADATA(IntentPickerLabelButton, views::LabelButton)
 END_METADATA
 
+class IntentPickerAppListView
+    : public IntentPickerBubbleView::IntentPickerAppsView {
+ public:
+  METADATA_HEADER(IntentPickerAppListView);
+
+  IntentPickerAppListView(
+      const std::vector<IntentPickerBubbleView::AppInfo>& apps,
+      base::RepeatingCallback<void(size_t, bool)> selected_callback)
+      : selected_callback_(selected_callback) {
+    auto scrollable_view = std::make_unique<views::View>();
+    scrollable_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kVertical));
+    scrollable_view->SetID(IntentPickerBubbleView::ViewId::kItemContainer);
+
+    for (size_t i = 0; i < apps.size(); i++) {
+      auto app_button = std::make_unique<IntentPickerLabelButton>(
+          base::BindRepeating(&IntentPickerAppListView::OnAppPressed,
+                              base::Unretained(this), i),
+          apps[i].icon_model, apps[i].display_name);
+      scrollable_view->AddChildViewAt(std::move(app_button), i);
+    }
+
+    SetBackgroundThemeColorId(ui::kColorBubbleBackground);
+    SetContents(std::move(scrollable_view));
+    DCHECK(!contents()->children().empty());
+    const int row_height =
+        contents()->children().front()->GetPreferredSize().height();
+    // TODO(djacobo): Replace this limit to correctly reflect the UI mocks,
+    // which now instead of limiting the results to 3.5 will allow whatever fits
+    // in 256pt. Using |kMaxAppResults| as a measure of how many apps we want to
+    // show.
+    ClipHeightTo(row_height, (apps::kMaxAppResults + 0.5) * row_height);
+  }
+
+  ~IntentPickerAppListView() override = default;
+
+  void SetSelectedIndex(size_t index) override {
+    SetSelectedAppIndex(index, nullptr);
+  }
+
+  size_t GetSelectedIndex() const override { return selected_app_index_; }
+
+  void OnKeyEvent(ui::KeyEvent* event) override {
+    if (!IsKeyboardCodeArrow(event->key_code()) ||
+        event->type() != ui::ET_KEY_RELEASED)
+      return;
+
+    int delta = 0;
+    switch (event->key_code()) {
+      case ui::VKEY_UP:
+        delta = -1;
+        break;
+      case ui::VKEY_DOWN:
+        delta = 1;
+        break;
+      case ui::VKEY_LEFT:
+        delta = base::i18n::IsRTL() ? 1 : -1;
+        break;
+      case ui::VKEY_RIGHT:
+        delta = base::i18n::IsRTL() ? -1 : 1;
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+
+    SetSelectedAppIndex(CalculateNextAppIndex(delta), nullptr);
+    AdjustScrollViewVisibleRegion();
+
+    ScrollView::OnKeyEvent(event);
+  }
+
+ private:
+  void OnAppPressed(size_t index, const ui::Event& event) {
+    SetSelectedAppIndex(index, &event);
+  }
+
+  void SetSelectedAppIndex(size_t index, const ui::Event* event) {
+    GetIntentPickerLabelButtonAt(selected_app_index_)
+        ->MarkAsUnselected(nullptr);
+    selected_app_index_ = index;
+    GetIntentPickerLabelButtonAt(selected_app_index_)->MarkAsSelected(event);
+
+    bool accepted = false;
+    if (event && ((event->IsMouseEvent() &&
+                   event->AsMouseEvent()->GetClickCount() == 2) ||
+                  (event->IsGestureEvent() &&
+                   event->AsGestureEvent()->details().tap_count() == 2))) {
+      accepted = true;
+    }
+
+    selected_callback_.Run(index, accepted);
+  }
+
+  size_t CalculateNextAppIndex(int delta) {
+    size_t size = contents()->children().size();
+    return static_cast<size_t>((selected_app_index_ + delta) % size);
+  }
+
+  void AdjustScrollViewVisibleRegion() {
+    views::ScrollBar* bar = vertical_scroll_bar();
+    if (bar) {
+      const int row_height =
+          contents()->children().front()->GetPreferredSize().height();
+      ScrollToPosition(bar, (selected_app_index_ - 1) * row_height);
+    }
+  }
+
+  IntentPickerLabelButton* GetIntentPickerLabelButtonAt(size_t index) {
+    const auto& children = contents()->children();
+    DCHECK_LT(index, children.size());
+    return static_cast<IntentPickerLabelButton*>(children[index]);
+  }
+
+  base::RepeatingCallback<void(size_t, bool)> selected_callback_;
+
+  size_t selected_app_index_ = 0;
+};
+
+BEGIN_METADATA(IntentPickerAppListView, views::ScrollView)
+ADD_PROPERTY_METADATA(size_t, SelectedIndex)
+END_METADATA
+
+}  // namespace
+
 // static
 IntentPickerBubbleView* IntentPickerBubbleView::intent_picker_bubble_ = nullptr;
 
@@ -126,7 +249,6 @@
       show_remember_selection, initiating_origin);
   if (highlighted_button)
     intent_picker_bubble_->SetHighlightedButton(highlighted_button);
-  intent_picker_bubble_->set_margins(gfx::Insets());
   intent_picker_bubble_->Initialize();
   views::Widget* widget =
       views::BubbleDialogDelegateView::CreateBubble(intent_picker_bubble_);
@@ -138,8 +260,7 @@
 
   DCHECK(intent_picker_bubble_->HasCandidates());
   widget->Show();
-  intent_picker_bubble_->GetIntentPickerLabelButtonAt(0)->MarkAsSelected(
-      nullptr);
+  intent_picker_bubble_->SetSelectedIndex(0);
   return widget;
 }
 
@@ -157,10 +278,10 @@
 void IntentPickerBubbleView::OnDialogAccepted() {
   bool should_persist = remember_selection_checkbox_ &&
                         remember_selection_checkbox_->GetChecked();
-  RunCallbackAndCloseBubble(app_info_[selected_app_tag_].launch_name,
-                            app_info_[selected_app_tag_].type,
-                            apps::IntentPickerCloseReason::OPEN_APP,
-                            should_persist);
+  auto selected_index = GetSelectedIndex();
+  RunCallbackAndCloseBubble(
+      app_info_[selected_index].launch_name, app_info_[selected_index].type,
+      apps::IntentPickerCloseReason::OPEN_APP, should_persist);
 }
 
 void IntentPickerBubbleView::OnDialogCancelled() {
@@ -184,6 +305,15 @@
   return true;
 }
 
+void IntentPickerBubbleView::SetSelectedIndex(size_t index) {
+  DCHECK_LT(index, app_info_.size());
+  apps_view_->SetSelectedIndex(index);
+}
+
+size_t IntentPickerBubbleView::GetSelectedIndex() const {
+  return apps_view_->GetSelectedIndex();
+}
+
 std::u16string IntentPickerBubbleView::GetWindowTitle() const {
   if (bubble_type_ == BubbleType::kClickToCall) {
     return l10n_util::GetStringUTF16(
@@ -237,6 +367,8 @@
   // intent_picker_helpers, they will get closed on each navigation start and
   // should stay open until after navigation finishes.
   SetCloseOnMainFrameOriginNavigation(bubble_type == BubbleType::kClickToCall);
+  // Margins are manually added in Initialize().
+  set_margins(gfx::Insets());
 }
 
 IntentPickerBubbleView::~IntentPickerBubbleView() {
@@ -251,77 +383,15 @@
                             false);
 }
 
-void IntentPickerBubbleView::AppButtonPressed(size_t index,
-                                              const ui::Event& event) {
-  SetSelectedAppIndex(index, &event);
-  if ((event.IsMouseEvent() && event.AsMouseEvent()->GetClickCount() == 2) ||
-      (event.IsGestureEvent() &&
-       event.AsGestureEvent()->details().tap_count() == 2)) {
+void IntentPickerBubbleView::OnAppSelected(size_t index, bool accepted) {
+  UpdateCheckboxState(index);
+
+  if (accepted) {
     AcceptDialog();
   }
 }
 
-void IntentPickerBubbleView::ArrowButtonPressed(size_t index) {
-  SetSelectedAppIndex(index, nullptr);
-  AdjustScrollViewVisibleRegion();
-}
-
-void IntentPickerBubbleView::OnKeyEvent(ui::KeyEvent* event) {
-  if (!IsKeyboardCodeArrow(event->key_code()) ||
-      event->type() != ui::ET_KEY_RELEASED)
-    return;
-
-  int delta = 0;
-  switch (event->key_code()) {
-    case ui::VKEY_UP:
-      delta = -1;
-      break;
-    case ui::VKEY_DOWN:
-      delta = 1;
-      break;
-    case ui::VKEY_LEFT:
-      delta = base::i18n::IsRTL() ? 1 : -1;
-      break;
-    case ui::VKEY_RIGHT:
-      delta = base::i18n::IsRTL() ? -1 : 1;
-      break;
-    default:
-      NOTREACHED();
-      break;
-  }
-  ArrowButtonPressed(CalculateNextAppIndex(delta));
-
-  View::OnKeyEvent(event);
-}
-
 void IntentPickerBubbleView::Initialize() {
-  // Creates a view to hold the views for each app.
-  auto scrollable_view = std::make_unique<views::View>();
-  scrollable_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
-  scrollable_view->SetID(ViewId::kItemContainer);
-
-  for (size_t i = 0; i < app_info_.size(); i++) {
-    auto app_button = std::make_unique<IntentPickerLabelButton>(
-        base::BindRepeating(&IntentPickerBubbleView::AppButtonPressed,
-                            base::Unretained(this), i),
-        app_info_[i].icon_model, app_info_[i].display_name);
-    scrollable_view->AddChildViewAt(std::move(app_button), i);
-  }
-
-  auto scroll_view = std::make_unique<views::ScrollView>();
-  scroll_view->SetBackgroundThemeColorId(ui::kColorBubbleBackground);
-  scroll_view->SetContents(std::move(scrollable_view));
-  DCHECK(!scroll_view->contents()->children().empty());
-  const int row_height =
-      scroll_view->contents()->children().front()->GetPreferredSize().height();
-  // TODO(djacobo): Replace this limit to correctly reflect the UI mocks, which
-  // now instead of limiting the results to 3.5 will allow whatever fits in
-  // 256pt. Using |kMaxAppResults| as a measure of how many apps we want to
-  // show.
-  scroll_view->ClipHeightTo(row_height,
-                            (apps::kMaxAppResults + 0.5) * row_height);
-
   const bool show_origin =
       initiating_origin_ &&
       !initiating_origin_->IsSameOriginWith(
@@ -363,7 +433,11 @@
     subtitle->SetMaximumWidth(kMaxDialogWidth - insets.width());
   }
 
-  scroll_view_ = AddChildView(std::move(scroll_view));
+  // Create a container for all of the individual app views.
+  auto list_view = std::make_unique<IntentPickerAppListView>(
+      app_info_, base::BindRepeating(&IntentPickerBubbleView::OnAppSelected,
+                                     base::Unretained(this)));
+  apps_view_ = AddChildView(std::move(list_view));
 
   if (show_origin) {
     std::u16string origin_text = l10n_util::GetStringFUTF16(
@@ -391,16 +465,9 @@
             IDS_INTENT_PICKER_BUBBLE_VIEW_REMEMBER_SELECTION)));
     remember_selection_checkbox_->SetID(ViewId::kRememberCheckbox);
     remember_selection_checkbox_->SetProperty(views::kMarginsKey, insets);
-    UpdateCheckboxState();
   }
 }
 
-IntentPickerLabelButton* IntentPickerBubbleView::GetIntentPickerLabelButtonAt(
-    size_t index) {
-  const auto& children = scroll_view_->contents()->children();
-  return static_cast<IntentPickerLabelButton*>(children[index]);
-}
-
 bool IntentPickerBubbleView::HasCandidates() const {
   return !app_info_.empty();
 }
@@ -423,39 +490,10 @@
   }
 }
 
-void IntentPickerBubbleView::AdjustScrollViewVisibleRegion() {
-  const views::ScrollBar* bar = scroll_view_->vertical_scroll_bar();
-  if (bar) {
-    const int row_height = scroll_view_->contents()
-                               ->children()
-                               .front()
-                               ->GetPreferredSize()
-                               .height();
-    scroll_view_->ScrollToPosition(const_cast<views::ScrollBar*>(bar),
-                                   (selected_app_tag_ - 1) * row_height);
-  }
-}
-
-void IntentPickerBubbleView::SetSelectedAppIndex(size_t index,
-                                                 const ui::Event* event) {
-  DCHECK(HasCandidates());
-  DCHECK_LT(index, app_info_.size());
-
-  GetIntentPickerLabelButtonAt(selected_app_tag_)->MarkAsUnselected(nullptr);
-  selected_app_tag_ = index;
-  GetIntentPickerLabelButtonAt(selected_app_tag_)->MarkAsSelected(event);
-  UpdateCheckboxState();
-}
-
-size_t IntentPickerBubbleView::CalculateNextAppIndex(int delta) {
-  size_t size = scroll_view_->contents()->children().size();
-  return static_cast<size_t>((selected_app_tag_ + size + delta) % size);
-}
-
-void IntentPickerBubbleView::UpdateCheckboxState() {
+void IntentPickerBubbleView::UpdateCheckboxState(size_t index) {
   if (!remember_selection_checkbox_)
     return;
-  auto selected_app_type = app_info_[selected_app_tag_].type;
+  auto selected_app_type = app_info_[index].type;
   bool should_enable = true;
   if (selected_app_type == apps::PickerEntryType::kDevice) {
     // TODO(crbug.com/1000037): Allow persisting remote devices.
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.h b/chrome/browser/ui/views/intent_picker_bubble_view.h
index 6c16f44..10f590f 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.h
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.h
@@ -19,6 +19,7 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/gfx/image/image.h"
 #include "ui/views/animation/ink_drop_state.h"
+#include "ui/views/controls/scroll_view.h"
 #include "url/origin.h"
 
 namespace content {
@@ -31,12 +32,6 @@
 class Widget;
 }  // namespace views
 
-namespace ui {
-class Event;
-}  // namespace ui
-
-class IntentPickerLabelButton;
-
 // A bubble that displays a list of applications (icons and names), after the
 // list the UI displays a checkbox to allow the user remember the selection and
 // after that a couple of buttons for either using the selected app or just
@@ -108,6 +103,17 @@
 
   BubbleType bubble_type() const { return bubble_type_; }
 
+  void SetSelectedIndex(size_t index);
+  size_t GetSelectedIndex() const;
+
+  // A ScrollView which contains a list of apps. This view manages the selection
+  // state for the dialog.
+  class IntentPickerAppsView : public views::ScrollView {
+   public:
+    virtual void SetSelectedIndex(size_t index) = 0;
+    virtual size_t GetSelectedIndex() const = 0;
+  };
+
   const std::vector<AppInfo>& app_info_for_testing() const { return app_info_; }
 
  protected:
@@ -121,14 +127,9 @@
   // views::BubbleDialogDelegateView overrides:
   void OnWidgetDestroying(views::Widget* widget) override;
 
-  void AppButtonPressed(size_t index, const ui::Event& event);
-
-  // Similar to AppButtonPressed, except this controls the up/down/right/left
-  // input while focusing on the |scroll_view_|.
-  void ArrowButtonPressed(size_t index);
-
-  // ui::EventHandler overrides:
-  void OnKeyEvent(ui::KeyEvent* event) override;
+  // Called when the app at |index| is selected in the app list. If |accepted|
+  // is true, the dialog should be immediately accepted with that app selected.
+  void OnAppSelected(size_t index, bool accepted);
 
   void Initialize();
 
@@ -136,10 +137,6 @@
   void OnDialogCancelled();
   void OnDialogClosed();
 
-  // Retrieves the IntentPickerLabelButton* contained at position |index| from
-  // the internal ScrollView.
-  IntentPickerLabelButton* GetIntentPickerLabelButtonAt(size_t index);
-
   // Runs |intent_picker_cb_| and closes the current bubble view.
   void RunCallbackAndCloseBubble(const std::string& launch_name,
                                  apps::PickerEntryType entry_type,
@@ -152,18 +149,8 @@
   // return false.
   bool HasCandidates() const;
 
-  // Ensure the selected app is within the visible region of the ScrollView.
-  void AdjustScrollViewVisibleRegion();
-
-  // Set the new app selection, use the |event| (if provided) to show a more
-  // accurate ripple effect w.r.t. the user's input.
-  void SetSelectedAppIndex(size_t index, const ui::Event* event);
-
-  // Calculate the next app to select given the current selection and |delta|.
-  size_t CalculateNextAppIndex(int delta);
-
   // Updates whether the persistence checkbox is enabled or not.
-  void UpdateCheckboxState();
+  void UpdateCheckboxState(size_t index);
 
   // Clears this bubble from being considered the currently open bubble.
   void ClearIntentPickerBubbleView();
@@ -173,13 +160,10 @@
   // Callback used to respond to AppsNavigationThrottle.
   IntentPickerResponse intent_picker_cb_;
 
-  // Pre-select the first app on the list.
-  size_t selected_app_tag_ = 0;
-
-  raw_ptr<views::ScrollView> scroll_view_ = nullptr;
-
   std::vector<AppInfo> app_info_;
 
+  raw_ptr<IntentPickerAppsView> apps_view_ = nullptr;
+
   raw_ptr<views::Checkbox> remember_selection_checkbox_ = nullptr;
 
   // When true, enables an alternate layout which presents apps as a grid
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index dce1fa36..aaf0346 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -269,33 +269,6 @@
   EXPECT_FALSE(intent_picker_view->GetVisible());
 }
 
-IN_PROC_BROWSER_TEST_F(IntentPickerBubbleViewBrowserTest, DoubleClickOpensApp) {
-  auto app_id = InstallTestWebApp(GetAppUrlHost(), GetAppScopePath());
-
-  const GURL in_scope_url =
-      https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), in_scope_url));
-
-  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
-                                       "IntentPickerBubbleView");
-  GetIntentPickerIcon()->ExecuteForTesting();
-  waiter.WaitIfNeededAndGet();
-  ASSERT_TRUE(intent_picker_bubble());
-  EXPECT_TRUE(intent_picker_bubble()->GetVisible());
-
-  auto event_generator = ui::test::EventGenerator(
-      views::GetRootWindow(intent_picker_bubble()->GetWidget()));
-  auto* container = intent_picker_bubble()->GetViewByID(
-      IntentPickerBubbleView::ViewId::kItemContainer);
-  ASSERT_GT(container->children().size(), 0ul);
-  event_generator.MoveMouseTo(
-      container->children()[0]->GetBoundsInScreen().CenterPoint());
-  event_generator.DoubleClickLeftButton();
-
-  Browser* app_browser = BrowserList::GetInstance()->GetLastActive();
-  EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(app_browser, app_id));
-}
-
 INSTANTIATE_TEST_SUITE_P(
     All,
     IntentPickerBubbleViewBrowserTest,
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
index e25059d..117a6b7 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_unittest.cc
@@ -20,11 +20,13 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/views/chrome_views_test_base.h"
+#include "components/services/app_service/public/cpp/intent_util.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/image/image.h"
 #include "ui/strings/grit/ui_strings.h"
@@ -154,16 +156,23 @@
         .size();
   }
 
-  // Dummy method to be called upon bubble closing.
-  void OnBubbleClosed(const std::string& selected_app_package,
+  void OnBubbleClosed(const std::string& selected_app_launch_name,
                       apps::PickerEntryType entry_type,
                       apps::IntentPickerCloseReason close_reason,
-                      bool should_persist) {}
+                      bool should_persist) {
+    last_selected_launch_name_ = selected_app_launch_name;
+    last_close_reason_ = close_reason;
+    last_selection_should_persist_ = should_persist;
+  }
 
   raw_ptr<IntentPickerBubbleView> bubble_;
   raw_ptr<views::View> anchor_view_;
   std::vector<AppInfo> app_info_;
   std::unique_ptr<ui::test::EventGenerator> event_generator_;
+
+  std::string last_selected_launch_name_;
+  apps::IntentPickerCloseReason last_close_reason_;
+  bool last_selection_should_persist_;
 };
 
 // Verifies that we didn't set up an image for any LabelButton.
@@ -340,3 +349,98 @@
   ClickApp(2);
   ASSERT_TRUE(checkbox->GetEnabled());
 }
+
+TEST_F(IntentPickerBubbleViewTest, AcceptDialog) {
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id_2", "Web App");
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  ClickApp(1);
+  bubble_->AcceptDialog();
+
+  EXPECT_EQ(last_selected_launch_name_, "web_app_id_2");
+  EXPECT_FALSE(last_selection_should_persist_);
+  EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
+}
+
+TEST_F(IntentPickerBubbleViewTest, AcceptDialogWithRememberSelection) {
+  AddApp(apps::PickerEntryType::kArc, "arc_app_id", "ARC App");
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  views::Checkbox* checkbox = static_cast<views::Checkbox*>(
+      bubble_->GetViewByID(IntentPickerBubbleView::ViewId::kRememberCheckbox));
+  checkbox->SetChecked(true);
+
+  bubble_->AcceptDialog();
+
+  EXPECT_EQ(last_selected_launch_name_, "arc_app_id");
+  EXPECT_TRUE(last_selection_should_persist_);
+  EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
+}
+
+TEST_F(IntentPickerBubbleViewTest, CancelDialog) {
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id_2", "Web App");
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/true,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  ClickApp(1);
+  bubble_->CancelDialog();
+
+  EXPECT_EQ(last_selected_launch_name_, apps_util::kUseBrowserForLink);
+  EXPECT_FALSE(last_selection_should_persist_);
+  EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::STAY_IN_CHROME);
+}
+
+TEST_F(IntentPickerBubbleViewTest, CloseDialog) {
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id_1", "Web App");
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  bubble_->GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kLostFocus);
+
+  ASSERT_EQ(last_close_reason_,
+            apps::IntentPickerCloseReason::DIALOG_DEACTIVATED);
+}
+
+TEST_F(IntentPickerBubbleViewTest, KeyboardNavigation) {
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  GetButtonAtIndex(0)->RequestFocus();
+
+  event_generator_->PressAndReleaseKey(ui::VKEY_DOWN);
+  EXPECT_EQ(bubble_->GetSelectedIndex(), 1u);
+  event_generator_->PressAndReleaseKey(ui::VKEY_LEFT);
+  EXPECT_EQ(bubble_->GetSelectedIndex(), 0u);
+  // Pressing up/left from the first item should wrap around to the last item.
+  event_generator_->PressAndReleaseKey(ui::VKEY_UP);
+  EXPECT_EQ(bubble_->GetSelectedIndex(),
+            bubble_->app_info_for_testing().size() - 1);
+  // Pressing down/right from the last item should wrap around to the first
+  // item.
+  event_generator_->PressAndReleaseKey(ui::VKEY_RIGHT);
+  EXPECT_EQ(bubble_->GetSelectedIndex(), 0u);
+}
+
+TEST_F(IntentPickerBubbleViewTest, DoubleClickToAccept) {
+  AddApp(apps::PickerEntryType::kWeb, "web_app_id", "Web App");
+  CreateBubbleView(/*use_icons=*/false, /*show_stay_in_chrome=*/false,
+                   BubbleType::kLinkCapturing,
+                   /*initiating_origin=*/absl::nullopt);
+
+  event_generator_->MoveMouseTo(
+      GetButtonAtIndex(0)->GetBoundsInScreen().CenterPoint());
+  event_generator_->DoubleClickLeftButton();
+
+  EXPECT_EQ(last_selected_launch_name_, "web_app_id");
+  EXPECT_EQ(last_close_reason_, apps::IntentPickerCloseReason::OPEN_APP);
+}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view_unittest.cc
index 2ab3e82..4f6c28fc 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view_unittest.cc
@@ -25,6 +25,10 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/widget/widget.h"
 
+#if defined(USE_AURA)
+#include "ui/aura/env.h"
+#endif
+
 namespace {
 
 // An arbitrary index for the result view under test. Used to test the selection
@@ -59,18 +63,16 @@
 class OmniboxResultViewTest : public ChromeViewsTestBase {
  public:
   void SetUp() override {
-    ChromeViewsTestBase::SetUp();
-
-    // Create a widget and assign bounds to support calls to HitTestPoint.
-    widget_ = CreateTestWidget();
-
-    // Install |test_screen_| after superclass setup and widget creation; on Ash
-    // both these require the Screen to work well with the underlying Shell, and
-    // TestScreen has no knowledge of that.
+#if !defined(USE_AURA)
     test_screen_ = std::make_unique<display::test::TestScreen>();
     scoped_screen_override_ =
         std::make_unique<display::test::ScopedScreenOverride>(
             test_screen_.get());
+#endif
+    ChromeViewsTestBase::SetUp();
+
+    // Create a widget and assign bounds to support calls to HitTestPoint.
+    widget_ = CreateTestWidget();
 
     edit_model_ = std::make_unique<OmniboxEditModel>(
         nullptr, nullptr, std::make_unique<TestOmniboxClient>());
@@ -89,10 +91,10 @@
   }
 
   void TearDown() override {
-    scoped_screen_override_.reset();
-    test_screen_.reset();
     widget_.reset();
     ChromeViewsTestBase::TearDown();
+    scoped_screen_override_.reset();
+    test_screen_.reset();
   }
 
   // Also sets the fake screen's mouse cursor to 0, 0.
@@ -105,7 +107,11 @@
                                 int flags,
                                 float x,
                                 float y) {
+#if !defined(USE_AURA)
     test_screen_->set_cursor_screen_point(gfx::Point(x, y));
+#else
+    aura::Env::GetInstance()->SetLastMouseLocation(gfx::Point(x, y));
+#endif
     return ui::MouseEvent(type, gfx::Point(x, y), gfx::Point(),
                           ui::EventTimeForNow(), flags, 0);
   }
diff --git a/chrome/browser/ui/views/sharing_hub/preview_view.cc b/chrome/browser/ui/views/sharing_hub/preview_view.cc
index 4137f98..eb10a318 100644
--- a/chrome/browser/ui/views/sharing_hub/preview_view.cc
+++ b/chrome/browser/ui/views/sharing_hub/preview_view.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/share/share_features.h"
 #include "chrome/browser/ui/sharing_hub/sharing_hub_bubble_controller.h"
+#include "components/url_formatter/elide_url.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/flex_layout.h"
@@ -50,6 +51,36 @@
   }
 }
 
+class UrlLabel : public views::Label {
+ public:
+  UrlLabel(GURL url, int context, int style)
+      : views::Label(base::UTF8ToUTF16(url.spec()), context, style), url_(url) {
+    // Never use the elided URL for the accessible name or tooltip - both of
+    // these are allowed to be of arbitrary length (since they aren't
+    // constrained by the visual layout) and should give the user the full URL.
+    SetAccessibleName(GetText());
+    SetTooltipText(GetText());
+  }
+  ~UrlLabel() override = default;
+
+  void OnBoundsChanged(const gfx::Rect& previous) override {
+    // Danger! Do not repurpose this behavior for your own use!
+    //
+    // It is safe to change the text in response to a layout like this *only*
+    // because the UrlLabel is included in a fixed-width bubble. If the bubble
+    // instead had variable width, or this view's width could otherwise change,
+    // then it would be very easy to get into an infinite loop between setting
+    // the text (thus prompting a relayout) and the layout setting the width
+    // (thus causing the text to be set again). Since the bubble containing the
+    // UrlLabel has a fixed width, this infinite recurrence can't happen.
+    views::Label::OnBoundsChanged(previous);
+    SetText(url_formatter::ElideUrl(url_, font_list(), width()));
+  }
+
+ private:
+  GURL url_;
+};
+
 }  // namespace
 
 // This view uses two nested FlexLayouts, a horizontal outer one and a vertical
@@ -93,9 +124,12 @@
   // here.
   title_ = labels_container->AddChildView(std::make_unique<views::Label>(
       attempt.title, views::style::CONTEXT_DIALOG_TITLE));
-  url_ = labels_container->AddChildView(std::make_unique<views::Label>(
-      base::UTF8ToUTF16(attempt.url.spec()), views::style::CONTEXT_DIALOG_TITLE,
+  title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+
+  url_ = labels_container->AddChildView(std::make_unique<UrlLabel>(
+      attempt.url, views::style::CONTEXT_DIALOG_TITLE,
       views::style::STYLE_HINT));
+  url_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 }
 
 PreviewView::~PreviewView() = default;
diff --git a/chrome/browser/ui/views/test/view_event_test_base.cc b/chrome/browser/ui/views/test/view_event_test_base.cc
index 54c917a..fed341ee 100644
--- a/chrome/browser/ui/views/test/view_event_test_base.cc
+++ b/chrome/browser/ui/views/test/view_event_test_base.cc
@@ -14,13 +14,13 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "mojo/core/embedder/embedder.h"
+#include "ui/display/screen.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
 #if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ui/display/screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 
 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_OZONE)
@@ -98,13 +98,12 @@
   // TODO(pkasting): Determine why the TestScreen in AuraTestHelper is
   // insufficient for these tests, then either bolster/replace it or fix the
   // tests.
-  DCHECK(!display::Screen::GetScreen());
+  DCHECK(!display::Screen::HasScreen());
 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && defined(USE_OZONE)
-  if (!display::Screen::GetScreen())
-    display::Screen::SetScreenInstance(
-        views::test::TestDesktopScreenOzone::GetInstance());
+  if (!display::Screen::HasScreen())
+    screen_ = views::test::TestDesktopScreenOzone::Create();
 #endif
-  if (!display::Screen::GetScreen())
+  if (!display::Screen::HasScreen())
     screen_ = views::CreateDesktopScreen();
 #endif
 }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 21e546e..813eb81 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -416,7 +416,6 @@
   if (GetBubbleFrameView()->GetWidget()->closed_reason() ==
       views::Widget::ClosedReason::kCloseButtonClicked) {
     model_->DeclineTranslation();
-    translate::ReportUiAction(translate::CLOSE_BUTTON_CLICKED);
     model_->ReportUIInteraction(translate::UIInteraction::kCloseUIExplicitly);
   } else {
     model_->ReportUIInteraction(translate::UIInteraction::kCloseUILostFocus);
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 35c8486e..cd24f0f 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -31,6 +31,7 @@
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/interaction/interaction_sequence.h"
+#include "ui/color/color_id.h"
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/view.h"
 #include "ui/views/view_utils.h"
@@ -89,29 +90,24 @@
 
   // These methods return color codes that will be handled by the app's theming
   // system.
-  int GetHelpBubbleBackgroundColor() const override {
-    return ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_BACKGROUND;
-  }
-  int GetHelpBubbleForegroundColor() const override {
-    return ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_FOREGROUND;
-  }
-  int GetHelpBubbleDefaultButtonBackgroundColor() const override {
-    return ThemeProperties::
-        COLOR_FEATURE_PROMO_BUBBLE_DEFAULT_BUTTON_BACKGROUND;
-  }
-  int GetHelpBubbleDefaultButtonForegroundColor() const override {
-    return ThemeProperties::
-        COLOR_FEATURE_PROMO_BUBBLE_DEFAULT_BUTTON_FOREGROUND;
-  }
-  int GetHelpBubbleButtonBorderColor() const override {
-    return ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_BUTTON_BORDER;
-  }
-  int GetHelpBubbleCloseButtonInkDropColor() const override {
-    return ThemeProperties::COLOR_FEATURE_PROMO_BUBBLE_CLOSE_BUTTON_INK_DROP;
-  }
   ui::ColorId GetHelpBubbleBackgroundColorId() const override {
     return kColorFeaturePromoBubbleBackground;
   }
+  ui::ColorId GetHelpBubbleForegroundColorId() const override {
+    return kColorFeaturePromoBubbleForeground;
+  }
+  ui::ColorId GetHelpBubbleDefaultButtonBackgroundColorId() const override {
+    return kColorFeaturePromoBubbleDefaultButtonBackground;
+  }
+  ui::ColorId GetHelpBubbleDefaultButtonForegroundColorId() const override {
+    return kColorFeaturePromoBubbleDefaultButtonForeground;
+  }
+  ui::ColorId GetHelpBubbleButtonBorderColorId() const override {
+    return kColorFeaturePromoBubbleButtonBorder;
+  }
+  ui::ColorId GetHelpBubbleCloseButtonInkDropColorId() const override {
+    return kColorFeaturePromoBubbleCloseButtonInkDrop;
+  }
 };
 
 }  // namespace
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
new file mode 100644
index 0000000..17096021
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
@@ -0,0 +1,141 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/page_action/page_action_icon_type.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_prefs_utils.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/tracker.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/dialog_model_field.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/bubble/bubble_dialog_model_host.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace chrome {
+
+void ShowWebAppDetailedInstallDialog(
+    content::WebContents* web_contents,
+    std::unique_ptr<WebAppInstallInfo> install_info,
+    chrome::AppInstallationAcceptanceCallback callback,
+    chrome::PwaInProductHelpState iph_state) {
+  content::BrowserContext* browser_context = web_contents->GetBrowserContext();
+  PrefService* const prefs =
+      Profile::FromBrowserContext(browser_context)->GetPrefs();
+
+  feature_engagement::Tracker* const tracker =
+      feature_engagement::TrackerFactory::GetForBrowserContext(browser_context);
+
+  constexpr int kIconSize = 32;
+
+  gfx::ImageSkia image(std::make_unique<WebAppInfoImageSource>(
+                           kIconSize, install_info->icon_bitmaps.any),
+                       gfx::Size(kIconSize, kIconSize));
+
+  auto title = install_info->title;
+  auto description = install_info->description;
+
+  auto delegate =
+      std::make_unique<web_app::WebAppDetailedInstallDialogDelegate>(
+          web_contents, std::move(install_info), std::move(callback),
+          std::move(iph_state), prefs, tracker);
+
+  auto* delegate_ptr = delegate.get();
+
+  auto dialog_model =
+      ui::DialogModel::Builder(std::move(delegate))
+          .SetIcon(ui::ImageModel::FromImageSkia(image))
+          .SetTitle(title)  // TODO(pbos): Add secondary-title support for
+                            // base::UTF8ToUTF16(install_info->start_url.host())
+          .AddBodyText(ui::DialogModelLabel(description))
+          .AddOkButton(
+              base::BindOnce(
+                  &web_app::WebAppDetailedInstallDialogDelegate::OnAccept,
+                  base::Unretained(delegate_ptr)),
+              l10n_util::GetStringUTF16(IDS_INSTALL))
+          .AddCancelButton(base::BindOnce(
+              &web_app::WebAppDetailedInstallDialogDelegate::OnCancel,
+              base::Unretained(delegate_ptr)))
+          .OverrideShowCloseButton(false)
+          .Build();
+
+  auto dialog = views::BubbleDialogModelHost::CreateModal(
+      std::move(dialog_model), ui::MODAL_TYPE_CHILD);
+
+  constrained_window::ShowWebModalDialogViews(dialog.release(), web_contents);
+}
+
+}  // namespace chrome
+
+namespace web_app {
+
+WebAppDetailedInstallDialogDelegate::WebAppDetailedInstallDialogDelegate(
+    content::WebContents* web_contents,
+    std::unique_ptr<WebAppInstallInfo> web_app_info,
+    chrome::AppInstallationAcceptanceCallback callback,
+    chrome::PwaInProductHelpState iph_state,
+    PrefService* prefs,
+    feature_engagement::Tracker* tracker)
+    : web_contents_(web_contents),
+      install_info_(std::move(web_app_info)),
+      callback_(std::move(callback)),
+      iph_state_(std::move(iph_state)),
+      prefs_(prefs),
+      tracker_(tracker) {
+  DCHECK(install_info_);
+  DCHECK(prefs_);
+}
+
+WebAppDetailedInstallDialogDelegate::~WebAppDetailedInstallDialogDelegate() {
+  // TODO(crbug.com/1327363): move this to dialog->SetHighlightedButton.
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
+  if (!browser)
+    return;
+
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
+  // Dehighlight the install icon when this dialog is closed.
+  browser_view->toolbar_button_provider()
+      ->GetPageActionIconView(PageActionIconType::kPwaInstall)
+      ->SetHighlighted(false);
+}
+
+void WebAppDetailedInstallDialogDelegate::OnAccept() {
+  if (iph_state_ == chrome::PwaInProductHelpState::kShown) {
+    web_app::AppId app_id = web_app::GenerateAppId(install_info_->manifest_id,
+                                                   install_info_->start_url);
+    web_app::RecordInstallIphInstalled(prefs_, app_id);
+    tracker_->NotifyEvent(feature_engagement::events::kDesktopPwaInstalled);
+  }
+
+  std::move(callback_).Run(true, std::move(install_info_));
+}
+
+void WebAppDetailedInstallDialogDelegate::OnCancel() {
+  if (iph_state_ == chrome::PwaInProductHelpState::kShown && install_info_) {
+    web_app::AppId app_id = web_app::GenerateAppId(install_info_->manifest_id,
+                                                   install_info_->start_url);
+    web_app::RecordInstallIphIgnored(prefs_, app_id, base::Time::Now());
+  }
+
+  std::move(callback_).Run(false, std::move(install_info_));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.h b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.h
new file mode 100644
index 0000000..69f1d6b
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.h
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_DETAILED_INSTALL_DIALOG_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_DETAILED_INSTALL_DIALOG_H_
+
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/views/view.h"
+
+class PrefService;
+
+namespace content {
+class WebContents;
+}
+
+namespace feature_engagement {
+class Tracker;
+}
+
+namespace web_app {
+
+class WebAppDetailedInstallDialogDelegate : public ui::DialogModelDelegate {
+ public:
+  WebAppDetailedInstallDialogDelegate(
+      content::WebContents* web_contents,
+      std::unique_ptr<WebAppInstallInfo> install_info,
+      chrome::AppInstallationAcceptanceCallback callback,
+      chrome::PwaInProductHelpState iph_state,
+      PrefService* prefs,
+      feature_engagement::Tracker* tracker);
+
+  ~WebAppDetailedInstallDialogDelegate() override;
+
+  void OnAccept();
+  void OnCancel();
+
+ private:
+  raw_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<WebAppInstallInfo> install_info_;
+  chrome::AppInstallationAcceptanceCallback callback_;
+  chrome::PwaInProductHelpState iph_state_;
+  raw_ptr<PrefService> prefs_;
+  raw_ptr<feature_engagement::Tracker> tracker_;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_DETAILED_INSTALL_DIALOG_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog_browsertest.cc
new file mode 100644
index 0000000..fe417187
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog_browsertest.cc
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.h"
+#include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
+#include "content/public/test/browser_test.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace web_app {
+
+class WebAppDetailedInstallDialogBrowserTest : public DialogBrowserTest {
+ public:
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    auto install_info = std::make_unique<WebAppInstallInfo>();
+    install_info->title = u"test";
+    install_info->description = u"This is a test app";
+
+    AddGeneratedIcon(&install_info->icon_bitmaps.any, kIconSize, kIconColor);
+
+    chrome::ShowWebAppDetailedInstallDialog(
+        browser()->tab_strip_model()->GetWebContentsAt(0),
+        std::move(install_info), base::DoNothing(),
+        chrome::PwaInProductHelpState::kNotShown);
+  }
+
+ private:
+  static constexpr int kIconSize = 40;
+  static constexpr SkColor kIconColor = SK_ColorGREEN;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppDetailedInstallDialogBrowserTest,
+                       InvokeUi_Default) {
+  ShowAndVerifyUi();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index 9cdc5010..93742dc2 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/web_applications/web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "components/webapps/browser/banners/app_banner_manager.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/navigation_entry.h"
@@ -47,10 +48,18 @@
   switch (flow) {
     case WebAppInstallFlow::kInstallSite:
       web_app_info->user_display_mode = UserDisplayMode::kStandalone;
-      chrome::ShowPWAInstallBubble(
-          initiator_web_contents, std::move(web_app_info),
-          std::move(web_app_acceptance_callback), iph_state);
-      return;
+      if (base::FeatureList::IsEnabled(
+              features::kDesktopPWAsDetailedInstallDialog)) {
+        chrome::ShowWebAppDetailedInstallDialog(
+            initiator_web_contents, std::move(web_app_info),
+            std::move(web_app_acceptance_callback), iph_state);
+        return;
+      } else {
+        chrome::ShowPWAInstallBubble(
+            initiator_web_contents, std::move(web_app_info),
+            std::move(web_app_acceptance_callback), iph_state);
+        return;
+      }
     case WebAppInstallFlow::kCreateShortcut:
       chrome::ShowWebAppInstallDialog(initiator_web_contents,
                                       std::move(web_app_info),
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
index 432117a..9bf8232 100644
--- a/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc
@@ -419,6 +419,12 @@
           IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(),
           GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
   html_source->AddString(
+      "crostiniSubtextNotSupported",
+      l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED,
+          ui::GetChromeOSDeviceName(),
+          GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
+  html_source->AddString(
       "crostiniArcAdbPowerwashRequiredSublabel",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_CROSTINI_ARC_ADB_POWERWASH_REQUIRED_SUBLABEL,
diff --git a/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc b/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc
index 4fe4c389..816a2df2 100644
--- a/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/metrics_reporting_handler_unittest.cc
@@ -43,9 +43,9 @@
     ASSERT_EQ(local_state(), g_browser_process->local_state());
     EXPECT_TRUE(test_web_ui()->call_data().empty());
 
-    base::ListValue args;
-    args.Append(std::make_unique<base::Value>(1));
-    handler()->HandleGetMetricsReporting(args.GetList());
+    base::Value::List args;
+    args.Append(1);
+    handler()->HandleGetMetricsReporting(args);
 
     EXPECT_TRUE(handler()->IsJavascriptAllowed());
     EXPECT_EQ(1u, test_web_ui()->call_data().size());
diff --git a/chrome/browser/ui/window_sizer/DEPS b/chrome/browser/ui/window_sizer/DEPS
index 046d8fd0..ea16c4459 100644
--- a/chrome/browser/ui/window_sizer/DEPS
+++ b/chrome/browser/ui/window_sizer/DEPS
@@ -1,6 +1,5 @@
 specific_include_rules = {
-  ".*test.*": [
-   "!ash",
-   "+ash/public",
+  ".*chromeos.*test.*": [
+   "+ash",
   ],
 }
diff --git a/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
index a29e51a..90696be 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_chromeos_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/window_sizer/window_sizer_chromeos.h"
 
 #include "ash/public/cpp/window_properties.h"
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
@@ -45,16 +47,30 @@
   void GetWindowBounds(const Browser* browser,
                        const gfx::Rect& passed_in,
                        int64_t display_id,
+                       const gfx::Rect& bounds,
+                       const gfx::Rect& work_area,
+                       Source source,
                        gfx::Rect* out_bounds) {
     auto state_provider = std::make_unique<TestStateProvider>();
-    state_provider->SetPersistentState(gfx::Rect(), gfx::Rect(),
-                                       ui::SHOW_STATE_DEFAULT);
+    if (source == PERSISTED) {
+      state_provider->SetPersistentState(bounds, work_area,
+                                         ui::SHOW_STATE_DEFAULT);
+    } else {
+      DCHECK_EQ(source, DEFAULT);
+    }
     display::Screen::GetScreen()->SetDisplayForNewWindows(display_id);
 
     ui::WindowShowState ignored;
     WindowSizer::GetBrowserWindowBoundsAndShowState(
         std::move(state_provider), passed_in, browser, out_bounds, &ignored);
   }
+  void GetWindowBounds(const Browser* browser,
+                       const gfx::Rect& passed_in,
+                       int64_t display_id,
+                       gfx::Rect* out_bounds = nullptr) {
+    GetWindowBounds(browser, passed_in, display_id, gfx::Rect(), gfx::Rect(),
+                    PERSISTED, out_bounds);
+  }
 
   // Returns browser window |out_bounds| and |out_show_state| for simulated
   // persisted and last-active window bounds, work area, show state, etc.
@@ -124,9 +140,13 @@
 
 }  // namespace
 
+// WindowSizerChromeOSNoAshTest are the tests that does not require ash
+// environment, thus it can use WindowSizerTestUtil that create a TestScreen
+// inside.
+
 // Test that the window is sized appropriately for the first run experience
 // where the default window bounds calculation is invoked.
-TEST_F(WindowSizerChromeOSTest, DefaultSizeCase) {
+TEST(WindowSizerChromeOSNoAshTest, DefaultSizeCase) {
   {
     // 4:3 monitor case, 1024x768, no taskbar.
     gfx::Rect window_bounds;
@@ -237,7 +257,7 @@
 
 // Test that the next opened window is positioned appropriately given the
 // bounds of an existing window of the same type.
-TEST_F(WindowSizerChromeOSTest, LastWindowBoundsCase) {
+TEST(WindowSizerChromeOS2NoAshTest, LastWindowBoundsCase) {
   {
     // Normal, in the middle of the screen somewhere.
     gfx::Rect window_bounds;
@@ -294,8 +314,8 @@
   }
 }
 
-TEST_F(WindowSizerChromeOSTest,
-       LastWindowOffscreenWithNonAggressiveRepositioning) {
+TEST(WindowSizerChromeOSNoAshTest,
+     LastWindowOffscreenWithNonAggressiveRepositioning) {
   {
     // Taskbar on left.
     gfx::Rect window_bounds;
@@ -370,6 +390,12 @@
 
 // Test the placement of newly created windows.
 TEST_F(WindowSizerChromeOSTest, PlaceNewWindows) {
+  UpdateDisplay("1600x1200");
+  auto* shelf = ash::Shell::GetPrimaryRootWindowController()->shelf();
+  shelf->SetAutoHideBehavior(ash::ShelfAutoHideBehavior::kAlways);
+
+  int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+
   // Create a browser to pass into the WindowSizerTestUtil::GetWindowBounds
   // function.
   Browser::CreateParams native_params(&profile_, true);
@@ -395,10 +421,9 @@
     Browser::CreateParams params_popup(Browser::TYPE_POPUP, &profile_, true);
     auto new_popup = CreateWindowlessBrowser(params_popup);
     gfx::Rect window_bounds;
-    WindowSizerTestUtil::GetWindowBounds(
-        p1600x1200, p1600x1200, gfx::Rect(), gfx::Rect(50, 100, 300, 150),
-        bottom_s1600x1200, PERSISTED, new_popup.get(), gfx::Rect(),
-        &window_bounds);
+    GetWindowBounds(new_popup.get(), gfx::Rect(), display_id,
+                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200, PERSISTED,
+                    &window_bounds);
     EXPECT_EQ("50,100 300x150", window_bounds.ToString());
   }
 
@@ -406,19 +431,17 @@
   {
     // If a window is there but not shown the persisted default should be used.
     gfx::Rect window_bounds;
-    WindowSizerTestUtil::GetWindowBounds(
-        p1600x1200, p1600x1200, gfx::Rect(), gfx::Rect(50, 100, 300, 150),
-        bottom_s1600x1200, PERSISTED, browser.get(), gfx::Rect(),
-        &window_bounds);
+    GetWindowBounds(browser.get(), gfx::Rect(), display_id,
+                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200, PERSISTED,
+                    &window_bounds);
     EXPECT_EQ("50,100 300x150", window_bounds.ToString());
   }
 
   {
     // If a window is there but not shown the default should be returned.
     gfx::Rect window_bounds;
-    WindowSizerTestUtil::GetWindowBounds(
-        p1600x1200, p1600x1200, gfx::Rect(), gfx::Rect(), bottom_s1600x1200,
-        DEFAULT, browser.get(), gfx::Rect(), &window_bounds);
+    GetWindowBounds(browser.get(), gfx::Rect(), display_id, gfx::Rect(),
+                    bottom_s1600x1200, DEFAULT, &window_bounds);
     // Note: We need to also take the defaults maximum width into account here
     // since that might get used if the resolution is too big.
     EXPECT_EQ(
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 58fd3d52..c749ca0 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -97,6 +97,8 @@
     "policy/web_app_settings_policy_handler.h",
     "preinstalled_app_install_features.cc",
     "preinstalled_app_install_features.h",
+    "preinstalled_web_app_config_utils.cc",
+    "preinstalled_web_app_config_utils.h",
     "preinstalled_web_app_manager.cc",
     "preinstalled_web_app_manager.h",
     "preinstalled_web_app_utils.cc",
diff --git a/chrome/browser/web_applications/preinstalled_web_app_config_utils.cc b/chrome/browser/web_applications/preinstalled_web_app_config_utils.cc
new file mode 100644
index 0000000..93c220a
--- /dev/null
+++ b/chrome/browser/web_applications/preinstalled_web_app_config_utils.cc
@@ -0,0 +1,139 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_switches.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/common/chrome_paths_lacros.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+namespace web_app {
+
+namespace {
+
+const base::FilePath* g_config_dir_for_testing = nullptr;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// The sub-directory of the extensions directory in which to scan for external
+// web apps (as opposed to external extensions or external ARC apps).
+const base::FilePath::CharType kWebAppsSubDirectory[] =
+    FILE_PATH_LITERAL("web_apps");
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+base::FilePath GetPreinstalledWebAppConfigDirFromDefaultPaths(
+    Profile* profile) {
+  if (g_config_dir_for_testing) {
+    return *g_config_dir_for_testing;
+  }
+
+  base::FilePath web_apps_dir;
+  if (chrome::GetPreinstalledWebAppConfigPath(&web_apps_dir))
+    return web_apps_dir;
+  return base::FilePath();
+}
+
+base::FilePath GetPreinstalledWebAppExtraConfigDirFromDefaultPaths(
+    Profile* profile) {
+  base::FilePath extra_web_apps_dir;
+  if (chrome::GetPreinstalledWebAppExtraConfigPath(&extra_web_apps_dir))
+    return extra_web_apps_dir;
+  return base::FilePath();
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+}  // namespace
+
+const base::FilePath* GetPreinstalledWebAppConfigDirForTesting() {
+  return g_config_dir_for_testing;
+}
+
+void SetPreinstalledWebAppConfigDirForTesting(
+    const base::FilePath* config_dir) {
+  g_config_dir_for_testing = config_dir;
+}
+
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+base::FilePath GetPreinstalledWebAppConfigDirFromCommandLine(Profile* profile) {
+  std::string command_line_directory =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kPreinstalledWebAppsDir);
+  if (!command_line_directory.empty())
+    return base::FilePath::FromUTF8Unsafe(command_line_directory);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // As of mid 2018, only Chrome OS has default/external web apps, and
+  // chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS is only defined for OS_LINUX,
+  // which includes OS_CHROMEOS.
+
+  // Exclude sign-in and lock screen profiles.
+  if (!ash::ProfileHelper::IsRegularProfile(profile)) {
+    return {};
+  }
+
+  if (g_config_dir_for_testing) {
+    return *g_config_dir_for_testing;
+  }
+
+  // For manual testing, you can change s/STANDALONE/USER/, as writing to
+  // "$HOME/.config/chromium/test-user/.config/chromium/External
+  // Extensions/web_apps" does not require root ACLs, unlike
+  // "/usr/share/chromium/extensions/web_apps".
+  base::FilePath dir;
+  if (base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
+                             &dir)) {
+    return dir.Append(kWebAppsSubDirectory);
+  }
+
+  LOG(ERROR) << "base::PathService::Get failed";
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  return {};
+}
+
+base::FilePath GetPreinstalledWebAppExtraConfigDirFromCommandLine(
+    Profile* profile) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  base::FilePath config_dir =
+      GetPreinstalledWebAppConfigDirFromCommandLine(profile);
+  std::string extra_config_subdir =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          ash::switches::kExtraWebAppsDir);
+  if (config_dir.empty() || extra_config_subdir.empty())
+    return base::FilePath();
+  return config_dir.AppendASCII(extra_config_subdir);
+#else
+  return base::FilePath();
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
+
+base::FilePath GetPreinstalledWebAppConfigDir(Profile* profile) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  return GetPreinstalledWebAppConfigDirFromDefaultPaths(profile);
+#else
+  return GetPreinstalledWebAppConfigDirFromCommandLine(profile);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+}
+
+base::FilePath GetPreinstalledWebAppExtraConfigDir(Profile* profile) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  return GetPreinstalledWebAppExtraConfigDirFromDefaultPaths(profile);
+#else
+  return GetPreinstalledWebAppExtraConfigDirFromCommandLine(profile);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_web_app_config_utils.h b/chrome/browser/web_applications/preinstalled_web_app_config_utils.h
new file mode 100644
index 0000000..0bd79414
--- /dev/null
+++ b/chrome/browser/web_applications/preinstalled_web_app_config_utils.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APP_CONFIG_UTILS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APP_CONFIG_UTILS_H_
+
+#include "base/files/file_path.h"
+#include "build/chromeos_buildflags.h"
+
+class Profile;
+
+namespace web_app {
+
+const base::FilePath* GetPreinstalledWebAppConfigDirForTesting();
+void SetPreinstalledWebAppConfigDirForTesting(const base::FilePath* config_dir);
+
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+// The directory where default web app configs are stored.
+// Empty if not applicable.
+base::FilePath GetPreinstalledWebAppConfigDirFromCommandLine(Profile* profile);
+
+// The directory where additional web app configs are stored. This allows a
+// single Chrome OS system image to have device-specific apps for multiple
+// devices. Empty if not applicable.
+base::FilePath GetPreinstalledWebAppExtraConfigDirFromCommandLine(
+    Profile* profile);
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
+
+// The directory where default web app configs are stored.
+// Empty if not applicable.
+// As of mid 2018, only Chrome OS has default/external web apps.
+base::FilePath GetPreinstalledWebAppConfigDir(Profile* profile);
+
+// The directory where additional web app configs are stored.
+// Empty if not applicable.
+base::FilePath GetPreinstalledWebAppExtraConfigDir(Profile* profile);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APP_CONFIG_UTILS_H_
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index 57bfbdc..d073054 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -12,7 +12,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/feature_list.h"
@@ -24,7 +23,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
-#include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -39,6 +37,7 @@
 #include "chrome/browser/web_applications/externally_managed_app_manager.h"
 #include "chrome/browser/web_applications/file_utils_wrapper.h"
 #include "chrome/browser/web_applications/preinstalled_app_install_features.h"
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chrome/browser/web_applications/preinstalled_web_app_utils.h"
 #include "chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h"
 #include "chrome/browser/web_applications/user_uninstalled_preinstalled_web_app_prefs.h"
@@ -48,8 +47,6 @@
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -72,6 +69,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/lacros/lacros_service.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
@@ -79,17 +77,9 @@
 
 namespace {
 
-#if BUILDFLAG(IS_CHROMEOS)
-// The sub-directory of the extensions directory in which to scan for external
-// web apps (as opposed to external extensions or external ARC apps).
-const base::FilePath::CharType kWebAppsSubDirectory[] =
-    FILE_PATH_LITERAL("web_apps");
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 bool g_skip_startup_for_testing_ = false;
 bool g_bypass_offline_manifest_requirement_for_testing_ = false;
 bool g_override_previous_user_uninstall_for_testing_ = false;
-const base::FilePath* g_config_dir_for_testing = nullptr;
 const std::vector<base::Value>* g_configs_for_testing = nullptr;
 FileUtilsWrapper* g_file_utils_for_testing = nullptr;
 
@@ -436,20 +426,6 @@
   return absl::nullopt;
 }
 
-std::string GetConfigDirectoryFromCommandLine() {
-  return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-      switches::kPreinstalledWebAppsDir);
-}
-
-std::string GetExtraConfigSubdirectory() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-      ash::switches::kExtraWebAppsDir);
-#else
-  return std::string();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-}
-
 }  // namespace
 
 const char* PreinstalledWebAppManager::kHistogramEnabledCount =
@@ -494,11 +470,6 @@
   g_override_previous_user_uninstall_for_testing_ = true;
 }
 
-void PreinstalledWebAppManager::SetConfigDirForTesting(
-    const base::FilePath* config_dir) {
-  g_config_dir_for_testing = config_dir;
-}
-
 void PreinstalledWebAppManager::SetConfigsForTesting(
     const std::vector<base::Value>* configs) {
   g_configs_for_testing = configs;
@@ -596,8 +567,8 @@
     LoadedConfigs loaded_configs;
     for (const base::Value& config : *g_configs_for_testing) {
       auto file = base::FilePath(FILE_PATH_LITERAL("test.json"));
-      if (g_config_dir_for_testing) {
-        file = g_config_dir_for_testing->Append(file);
+      if (GetPreinstalledWebAppConfigDirForTesting()) {
+        file = GetPreinstalledWebAppConfigDirForTesting()->Append(file);
       }
 
       loaded_configs.configs.push_back(
@@ -612,16 +583,17 @@
     return;
   }
 
-  base::FilePath config_dir = GetConfigDir();
+  base::FilePath config_dir = GetPreinstalledWebAppConfigDir(profile_);
   if (config_dir.empty()) {
     std::move(callback).Run({});
     return;
   }
 
   std::vector<base::FilePath> config_dirs = {config_dir};
-  std::string extra_config_subdir = GetExtraConfigSubdirectory();
-  if (!extra_config_subdir.empty()) {
-    config_dirs.push_back(config_dir.AppendASCII(extra_config_subdir));
+  base::FilePath extra_config_dir =
+      GetPreinstalledWebAppExtraConfigDir(profile_);
+  if (!extra_config_dir.empty()) {
+    config_dirs.push_back(extra_config_dir);
   }
 
   base::ThreadPool::PostTaskAndReplyWithResult(
@@ -860,43 +832,6 @@
   }
 }
 
-base::FilePath PreinstalledWebAppManager::GetConfigDir() {
-  std::string command_line_directory = GetConfigDirectoryFromCommandLine();
-  if (!command_line_directory.empty())
-    return base::FilePath::FromUTF8Unsafe(command_line_directory);
-
-#if BUILDFLAG(IS_CHROMEOS)
-    // As of mid 2018, only Chrome OS has default/external web apps, and
-    // chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS is only defined for OS_LINUX,
-    // which includes OS_CHROMEOS.
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Exclude sign-in and lock screen profiles.
-  if (!ash::ProfileHelper::IsRegularProfile(profile_)) {
-    return {};
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-  if (g_config_dir_for_testing) {
-    return *g_config_dir_for_testing;
-  }
-
-  // For manual testing, you can change s/STANDALONE/USER/, as writing to
-  // "$HOME/.config/chromium/test-user/.config/chromium/External
-  // Extensions/web_apps" does not require root ACLs, unlike
-  // "/usr/share/chromium/extensions/web_apps".
-  base::FilePath dir;
-  if (base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
-                             &dir)) {
-    return dir.Append(kWebAppsSubDirectory);
-  }
-
-  LOG(ERROR) << "base::PathService::Get failed";
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
-  return {};
-}
-
 bool PreinstalledWebAppManager::IsNewUser() {
   PrefService* prefs = profile_->GetPrefs();
   return prefs->GetString(prefs::kWebAppsLastPreinstallSynchronizeVersion)
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.h b/chrome/browser/web_applications/preinstalled_web_app_manager.h
index 2c0a7482d..cfd5a5f 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.h
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.h
@@ -20,10 +20,6 @@
 #include "chrome/browser/web_applications/file_utils_wrapper.h"
 #include "url/gurl.h"
 
-namespace base {
-class FilePath;
-}
-
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -79,8 +75,10 @@
 
   static void SkipStartupForTesting();
   static void BypassOfflineManifestRequirementForTesting();
+
   static void OverridePreviousUserUninstallConfigForTesting();
   static void SetConfigDirForTesting(const base::FilePath* config_dir);
+
   static void SetConfigsForTesting(const std::vector<base::Value>* configs);
   static void SetFileUtilsForTesting(FileUtilsWrapper* file_utils);
 
@@ -147,10 +145,6 @@
           install_results,
       std::map<InstallUrl, bool> uninstall_results);
 
-  // The directory where default web app configs are stored.
-  // Empty if not applicable.
-  base::FilePath GetConfigDir();
-
   // Returns whether this is the first time we've deployed default apps on this
   // profile.
   bool IsNewUser();
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
index b5e2240..1a2640f4 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/preinstalled_app_install_features.h"
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h"
 #include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
 #include "chrome/browser/web_applications/test/test_file_utils.h"
@@ -218,7 +219,7 @@
       const GURL& install_url,
       base::StringPiece app_config_string) {
     base::FilePath test_config_dir(FILE_PATH_LITERAL("test_dir"));
-    PreinstalledWebAppManager::SetConfigDirForTesting(&test_config_dir);
+    SetPreinstalledWebAppConfigDirForTesting(&test_config_dir);
 
     base::FilePath source_root_dir;
     CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir));
@@ -255,7 +256,7 @@
             }));
     sync_run_loop.Run();
 
-    PreinstalledWebAppManager::SetConfigDirForTesting(nullptr);
+    SetPreinstalledWebAppConfigDirForTesting(nullptr);
     PreinstalledWebAppManager::SetFileUtilsForTesting(nullptr);
     PreinstalledWebAppManager::SetConfigsForTesting(nullptr);
 
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
index 681502f..5018b41 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/extension_management_test_util.h"
 #include "chrome/browser/web_applications/preinstalled_app_install_features.h"
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
@@ -49,6 +50,13 @@
 #include "components/user_manager/scoped_user_manager.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_paths_lacros.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#endif
+
 namespace web_app {
 
 namespace {
@@ -97,7 +105,7 @@
   }
 
  protected:
-  std::vector<ExternalInstallOptions> LoadApps(const char* test_dir,
+  std::vector<ExternalInstallOptions> LoadApps(base::StringPiece test_dir,
                                                Profile* profile = nullptr) {
     std::unique_ptr<TestingProfile> testing_profile;
     if (!profile) {
@@ -109,17 +117,8 @@
 #endif
     }
 
-    // Uses the chrome/test/data/web_app_default_apps/test_dir directory
-    // that holds the *.json data files from which tests should parse as app
-    // configs.
-    base::FilePath config_dir;
-    if (!base::PathService::Get(chrome::DIR_TEST_DATA, &config_dir)) {
-      ADD_FAILURE()
-          << "base::PathService::Get could not resolve chrome::DIR_TEST_DATA";
-    }
-    config_dir =
-        config_dir.AppendASCII("web_app_default_apps").AppendASCII(test_dir);
-    PreinstalledWebAppManager::SetConfigDirForTesting(&config_dir);
+    base::FilePath config_dir = GetConfigDir(test_dir);
+    SetPreinstalledWebAppConfigDirForTesting(&config_dir);
 
     auto preinstalled_web_app_manager =
         std::make_unique<PreinstalledWebAppManager>(profile);
@@ -139,7 +138,7 @@
         }));
     run_loop.Run();
 
-    PreinstalledWebAppManager::SetConfigDirForTesting(nullptr);
+    SetPreinstalledWebAppConfigDirForTesting(nullptr);
 
     return result;
   }
@@ -187,6 +186,26 @@
     return profile;
   }
 
+  void SetExtraWebAppsDir(base::StringPiece test_dir,
+                          base::StringPiece extra_web_apps_dir) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+        ash::switches::kExtraWebAppsDir, extra_web_apps_dir);
+#else
+    base::FilePath config_dir = GetConfigDir(test_dir);
+    auto params = crosapi::mojom::BrowserInitParams::New();
+    params->default_paths = crosapi::mojom::DefaultPaths::New();
+    params->default_paths->documents =
+        base::PathService::CheckedGet(chrome::DIR_USER_DOCUMENTS);
+    params->default_paths->downloads =
+        base::PathService::CheckedGet(chrome::DIR_DEFAULT_DOWNLOADS);
+    params->default_paths->preinstalled_web_app_config = config_dir;
+    params->default_paths->preinstalled_web_app_extra_config =
+        config_dir.AppendASCII(extra_web_apps_dir);
+    chrome::SetLacrosDefaultPathsFromInitParams(params.get());
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  }
+
   void VerifySetOfApps(Profile* profile, const std::set<GURL>& expectations) {
     const auto install_options_list = LoadApps(kUserTypesTestDir, profile);
     ASSERT_EQ(expectations.size(), install_options_list.size());
@@ -209,6 +228,18 @@
   ScopedTestingPreinstalledAppData preinstalled_web_app_override_;
 
  private:
+  base::FilePath GetConfigDir(base::StringPiece test_dir) {
+    // Uses the chrome/test/data/web_app_default_apps/test_dir directory
+    // that holds the *.json data files from which tests should parse as app
+    // configs.
+    base::FilePath config_dir;
+    if (!base::PathService::Get(chrome::DIR_TEST_DATA, &config_dir)) {
+      ADD_FAILURE()
+          << "base::PathService::Get could not resolve chrome::DIR_TEST_DATA";
+    }
+    return config_dir.AppendASCII("web_app_default_apps").AppendASCII(test_dir);
+  }
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::FakeChromeUserManager* user_manager() {
     return static_cast<ash::FakeChromeUserManager*>(
@@ -219,6 +250,8 @@
 
   // To support primary/non-primary users.
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
+
+  base::test::ScopedCommandLine command_line_;
 #endif
 
   // To support context of browser threads.
@@ -501,27 +534,19 @@
   EXPECT_TRUE(profile->IsChild());
   VerifySetOfApps(profile.get(), {GURL(kAppAllUrl), GURL(kAppChildUrl)});
 }
-#else
-// No app is expected for non-ChromeOS builds.
-TEST_F(PreinstalledWebAppManagerTest, NoApp) {
-  EXPECT_TRUE(LoadApps(kUserTypesTestDir, CreateProfile().get()).empty());
-}
-#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 TEST_F(PreinstalledWebAppManagerTest, NonPrimaryProfile) {
   VerifySetOfApps(CreateProfile().get(),
                   {GURL(kAppAllUrl), GURL(kAppUnmanagedUrl)});
 }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-// TODO(crbug.com/1252272): Enable extra web apps tests for Lacros.
 TEST_F(PreinstalledWebAppManagerTest, ExtraWebApps) {
   // The extra_web_apps directory contains two JSON files in different named
   // subdirectories. The --extra-web-apps-dir switch should control which
   // directory apps are loaded from.
-  base::test::ScopedCommandLine command_line;
-  command_line.GetProcessCommandLine()->AppendSwitchASCII(
-      ash::switches::kExtraWebAppsDir, "model1");
+  SetExtraWebAppsDir("extra_web_apps", "model1");
 
   const auto app_infos = LoadApps("extra_web_apps");
   EXPECT_EQ(1u, app_infos.size());
@@ -529,15 +554,18 @@
 }
 
 TEST_F(PreinstalledWebAppManagerTest, ExtraWebAppsNoMatchingDirectory) {
-  base::test::ScopedCommandLine command_line;
-  command_line.GetProcessCommandLine()->AppendSwitchASCII(
-      ash::switches::kExtraWebAppsDir, "model3");
+  SetExtraWebAppsDir("extra_web_apps", "model3");
 
   const auto app_infos = LoadApps("extra_web_apps");
   EXPECT_EQ(0u, app_infos.size());
   ExpectHistograms(/*enabled=*/0, /*disabled=*/0, /*errors=*/0);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#else
+// No app is expected for non-ChromeOS builds.
+TEST_F(PreinstalledWebAppManagerTest, NoApp) {
+  EXPECT_TRUE(LoadApps(kUserTypesTestDir, CreateProfile().get()).empty());
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_CHROMEOS)
 class DisabledPreinstalledWebAppManagerTest
diff --git a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
index 61e3183d..3bf70bb 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
@@ -10,6 +10,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/web_applications/preinstalled_app_install_features.h"
+#include "chrome/browser/web_applications/preinstalled_web_app_config_utils.h"
 #include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
 #include "chrome/browser/web_applications/test/fake_os_integration_manager.h"
 #include "chrome/browser/web_applications/test/with_crosapi_param.h"
@@ -30,7 +31,7 @@
   PreinstalledWebAppsBrowserTest() {
     PreinstalledWebAppManager::SkipStartupForTesting();
     // Ignore any default app configs on disk.
-    PreinstalledWebAppManager::SetConfigDirForTesting(&empty_path_);
+    SetPreinstalledWebAppConfigDirForTesting(&empty_path_);
     WebAppProvider::SetOsIntegrationManagerFactoryForTesting(
         [](Profile* profile) -> std::unique_ptr<OsIntegrationManager> {
           return std::make_unique<FakeOsIntegrationManager>(
@@ -38,6 +39,10 @@
         });
   }
 
+  ~PreinstalledWebAppsBrowserTest() override {
+    SetPreinstalledWebAppConfigDirForTesting(nullptr);
+  }
+
   void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
     InProcessBrowserTest::SetUpDefaultCommandLine(command_line);
 
diff --git a/chrome/browser/window_placement/window_placement_printing_interactive_uitest.cc b/chrome/browser/window_placement/window_placement_printing_interactive_uitest.cc
index 8efceba..b10fccc 100644
--- a/chrome/browser/window_placement/window_placement_printing_interactive_uitest.cc
+++ b/chrome/browser/window_placement/window_placement_printing_interactive_uitest.cc
@@ -25,6 +25,15 @@
 
 class WindowPlacementTest : public InProcessBrowserTest {
  public:
+  void SetUp() override {
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    display::Screen::SetScreenInstance(&screen_);
+    screen_.display_list().AddDisplay({1, gfx::Rect(0, 0, 803, 600)},
+                                      display::DisplayList::Type::PRIMARY);
+#endif
+    InProcessBrowserTest::SetUp();
+  }
+
   void SetUpOnMainThread() override {
     // Window placement features are only available on secure contexts.
     https_test_server_ = std::make_unique<net::EmbeddedTestServer>(
@@ -33,10 +42,20 @@
     ASSERT_TRUE(https_test_server_->Start());
   }
 
+  void TearDown() override {
+    InProcessBrowserTest::TearDown();
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    display::Screen::SetScreenInstance(nullptr);
+#endif
+  }
+
  protected:
   std::unique_ptr<net::EmbeddedTestServer> https_test_server_;
   base::test::ScopedFeatureList scoped_feature_list_{
       blink::features::kWindowPlacement};
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+  display::ScreenBase screen_;
+#endif
 };
 
 // TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
@@ -56,11 +75,6 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
       .UpdateDisplay("0+0-803x600");
-#else
-  display::ScreenBase screen;
-  screen.display_list().AddDisplay({1, gfx::Rect(0, 0, 803, 600)},
-                                   display::DisplayList::Type::PRIMARY);
-  display::test::ScopedScreenOverride screen_override(&screen);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
 
@@ -99,9 +113,9 @@
   display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
       .UpdateDisplay("0+0-807x600");
 #else
-  screen.display_list().UpdateDisplay({1, gfx::Rect(0, 0, 807, 600)},
-                                      display::DisplayList::Type::PRIMARY);
-  EXPECT_EQ(screen.display_list().displays().size(), 1u);
+  screen_.display_list().UpdateDisplay({1, gfx::Rect(0, 0, 807, 600)},
+                                       display::DisplayList::Type::PRIMARY);
+  EXPECT_EQ(screen_.display_list().displays().size(), 1u);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
 
@@ -116,9 +130,9 @@
   display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
       .UpdateDisplay("0+0-807x600,1000+0-804x600");
 #else
-  screen.display_list().AddDisplay({2, gfx::Rect(1000, 0, 804, 600)},
-                                   display::DisplayList::Type::NOT_PRIMARY);
-  EXPECT_EQ(screen.display_list().displays().size(), 2u);
+  screen_.display_list().AddDisplay({2, gfx::Rect(1000, 0, 804, 600)},
+                                    display::DisplayList::Type::NOT_PRIMARY);
+  EXPECT_EQ(screen_.display_list().displays().size(), 2u);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 9868acf1..2ca5cd8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1652982811-2fdf2c43be67d816ba181468f29ead94c38a3207.profdata
+chrome-linux-main-1653004718-550f92d458d9543873af601aef4d70240f640880.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index fe98ded..c99bdfb1 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1652982811-ad1c4ef9176e8eedd9baead64a3dbdc7f3a1f038.profdata
+chrome-mac-arm-main-1653004718-e355640cf3224109cd9cf7dc418dc3bc876c4f5b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index ba9c95f..f48da14e 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1652982811-7012145e83da8501e81be3da58aaba6d62c9623c.profdata
+chrome-mac-main-1653004718-c2ca067c3647bbce4d00287f02758d82633f0fa9.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index f4ba7db..aee8e8a 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1652982811-78a4c035bd06b470693241830b4b763a3d090d32.profdata
+chrome-win32-main-1653004718-c2433064b36fa3564a7f8b552448083cded1b1fc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7ce8c56..b40073c67 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1652982811-a29cc5640d9014852937387704bfc56637e8411a.profdata
+chrome-win64-main-1653004718-61b6082e36edd8b9bd57650fde09bf7ab12acb8e.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index dd21e7f4..ff727a14 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -284,6 +284,10 @@
 const base::Feature kDesktopPWAsEnforceWebAppSettingsPolicy{
     "DesktopPWAsEnforceWebAppSettingsPolicy", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables showing a detailed install dialog for user installs.
+const base::Feature kDesktopPWAsDetailedInstallDialog{
+    "DesktopPWAsDetailedInstallDialog", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables or disables Desktop PWAs to be auto-started on OS login.
 const base::Feature kDesktopPWAsRunOnOsLogin {
   "DesktopPWAsRunOnOsLogin",
@@ -307,6 +311,7 @@
 // Serves web app settings at chrome://app-settings/<app-id>.
 const base::Feature kDesktopPWAsWebAppSettingsPage{
     "DesktopPWAsWebAppSettingsPage", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Controls whether Chrome Apps are supported. See https://crbug.com/1221251.
 // If the feature is disabled, Chrome Apps continue to work. If enabled, Chrome
 // Apps will not launch and will be marked in the UI as deprecated.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index d73c00f..3ab6dea 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -208,6 +208,9 @@
 extern const base::Feature kDesktopPWAsFlashAppNameInsteadOfOrigin;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kDesktopPWAsDetailedInstallDialog;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDesktopPWAsRunOnOsLogin;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/chrome_paths_lacros.cc b/chrome/common/chrome_paths_lacros.cc
index 85d1ba68..3f26f06e 100644
--- a/chrome/common/chrome_paths_lacros.cc
+++ b/chrome/common/chrome_paths_lacros.cc
@@ -26,6 +26,8 @@
   base::FilePath android_files_dir;
   base::FilePath linux_files_dir;
   base::FilePath share_cache_dir;
+  base::FilePath preinstalled_web_app_config_dir;
+  base::FilePath preinstalled_web_app_extra_config_dir;
 };
 
 DefaultPaths& GetDefaultPaths() {
@@ -35,14 +37,17 @@
 
 }  // namespace
 
-void SetLacrosDefaultPaths(const base::FilePath& documents_dir,
-                           const base::FilePath& downloads_dir,
-                           const base::FilePath& drivefs,
-                           const base::FilePath& removable_media_dir,
-                           const base::FilePath& android_files_dir,
-                           const base::FilePath& linux_files_dir,
-                           const base::FilePath& ash_resources_dir,
-                           const base::FilePath& share_cache_dir) {
+void SetLacrosDefaultPaths(
+    const base::FilePath& documents_dir,
+    const base::FilePath& downloads_dir,
+    const base::FilePath& drivefs,
+    const base::FilePath& removable_media_dir,
+    const base::FilePath& android_files_dir,
+    const base::FilePath& linux_files_dir,
+    const base::FilePath& ash_resources_dir,
+    const base::FilePath& share_cache_dir,
+    const base::FilePath& preinstalled_web_app_config_dir,
+    const base::FilePath& preinstalled_web_app_extra_config_dir) {
   DCHECK(!documents_dir.empty());
   DCHECK(documents_dir.IsAbsolute());
   GetDefaultPaths().documents_dir = documents_dir;
@@ -60,6 +65,10 @@
   chromeos::lacros_paths::SetAshResourcesPath(ash_resources_dir);
 
   GetDefaultPaths().share_cache_dir = share_cache_dir;
+  GetDefaultPaths().preinstalled_web_app_config_dir =
+      preinstalled_web_app_config_dir;
+  GetDefaultPaths().preinstalled_web_app_extra_config_dir =
+      preinstalled_web_app_extra_config_dir;
 }
 
 void SetLacrosDefaultPathsFromInitParams(
@@ -86,11 +95,23 @@
     base::FilePath share_cache_dir;
     if (init_params->default_paths->share_cache.has_value())
       share_cache_dir = init_params->default_paths->share_cache.value();
+    base::FilePath preinstalled_web_app_config_dir;
+    if (init_params->default_paths->preinstalled_web_app_config.has_value()) {
+      preinstalled_web_app_config_dir =
+          init_params->default_paths->preinstalled_web_app_config.value();
+    }
+    base::FilePath preinstalled_web_app_extra_config_dir;
+    if (init_params->default_paths->preinstalled_web_app_extra_config
+            .has_value()) {
+      preinstalled_web_app_extra_config_dir =
+          init_params->default_paths->preinstalled_web_app_extra_config.value();
+    }
 
     chrome::SetLacrosDefaultPaths(
         init_params->default_paths->documents,
         init_params->default_paths->downloads, drivefs_dir, removable_media_dir,
-        android_files_dir, linux_files_dir, ash_resources_dir, share_cache_dir);
+        android_files_dir, linux_files_dir, ash_resources_dir, share_cache_dir,
+        preinstalled_web_app_config_dir, preinstalled_web_app_extra_config_dir);
   }
 }
 
@@ -193,4 +214,18 @@
   return true;
 }
 
+bool GetPreinstalledWebAppConfigPath(base::FilePath* result) {
+  if (GetDefaultPaths().preinstalled_web_app_config_dir.empty())
+    return false;
+  *result = GetDefaultPaths().preinstalled_web_app_config_dir;
+  return true;
+}
+
+bool GetPreinstalledWebAppExtraConfigPath(base::FilePath* result) {
+  if (GetDefaultPaths().preinstalled_web_app_extra_config_dir.empty())
+    return false;
+  *result = GetDefaultPaths().preinstalled_web_app_extra_config_dir;
+  return true;
+}
+
 }  // namespace chrome
diff --git a/chrome/common/chrome_paths_lacros.h b/chrome/common/chrome_paths_lacros.h
index bf52e76..95ea2b53 100644
--- a/chrome/common/chrome_paths_lacros.h
+++ b/chrome/common/chrome_paths_lacros.h
@@ -17,14 +17,17 @@
 // including documents, downloads, DriveFS, removable media, ARC storage
 // and Crostini's home directory. The paths are sent by ash-chrome and
 // are set early in lacros-chrome startup.
-void SetLacrosDefaultPaths(const base::FilePath& documents_dir,
-                           const base::FilePath& downloads_dir,
-                           const base::FilePath& drivefs,
-                           const base::FilePath& removable_media_dir,
-                           const base::FilePath& android_files_dir,
-                           const base::FilePath& linux_files_dir,
-                           const base::FilePath& ash_resources_dir,
-                           const base::FilePath& share_cache_dir);
+void SetLacrosDefaultPaths(
+    const base::FilePath& documents_dir,
+    const base::FilePath& downloads_dir,
+    const base::FilePath& drivefs,
+    const base::FilePath& removable_media_dir,
+    const base::FilePath& android_files_dir,
+    const base::FilePath& linux_files_dir,
+    const base::FilePath& ash_resources_dir,
+    const base::FilePath& share_cache_dir,
+    const base::FilePath& preinstalled_web_app_config_dir,
+    const base::FilePath& preinstalled_web_app_extra_config_dir);
 
 // Sets the default paths from BrowserInitParams received from ash on startup.
 void SetLacrosDefaultPathsFromInitParams(
@@ -43,6 +46,8 @@
 bool GetAndroidFilesPath(base::FilePath* result);
 bool GetLinuxFilesPath(base::FilePath* result);
 bool GetShareCachePath(base::FilePath* result);
+bool GetPreinstalledWebAppConfigPath(base::FilePath* result);
+bool GetPreinstalledWebAppExtraConfigPath(base::FilePath* result);
 }  // namespace chrome
 
 #endif  // CHROME_COMMON_CHROME_PATHS_LACROS_H_
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration.cc b/chrome/common/profiler/thread_profiler_platform_configuration.cc
index ae63ed7..704b9bd8 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration.cc
+++ b/chrome/common/profiler/thread_profiler_platform_configuration.cc
@@ -201,38 +201,29 @@
 
 double AndroidPlatformConfiguration::GetChildProcessEnableFraction(
     metrics::CallStackProfileParams::Process process) const {
-  DCHECK_NE(metrics::CallStackProfileParams::Process::kBrowser, process);
+  // Profile all processes in browser test mode since they're disabled
+  // otherwise.
+  if (browser_test_mode_enabled())
+    return DefaultPlatformConfiguration::GetChildProcessEnableFraction(process);
 
-  // Profile all supported processes in browser test mode.
-  if (browser_test_mode_enabled()) {
-    return 1.0;
-  }
+  if (process == metrics::CallStackProfileParams::Process::kRenderer)
+    return 0.4;
 
-  // TODO(https://crbug.com/1326430): Enable for all the default processes.
-  switch (process) {
-    case metrics::CallStackProfileParams::Process::kRenderer:
-      // There are empirically, on average, 1.3 renderer processes per browser
-      // process. This samples the renderer process at roughly the same
-      // frequency overall as the browser process.
-      // http://uma/p/chrome/timeline_v2?sid=39bc30a43a01d045204d0add05ad120a
-      return 0.75;
-
-    default:
-      return 0.0;
-  }
+  // TODO(https://crbug.com/1004855): Enable for all the default processes.
+  return 0.0;
 }
 
 bool AndroidPlatformConfiguration::IsEnabledForThread(
     metrics::CallStackProfileParams::Process process,
     metrics::CallStackProfileParams::Thread thread) const {
-  // TODO(https://crbug.com/1326430): Enable for all the default processes.
-  switch (process) {
-    case metrics::CallStackProfileParams::Process::kRenderer:
-      return thread == metrics::CallStackProfileParams::Thread::kMain;
-
-    default:
-      return false;
+  // Enable on renderer process main thread in production, for now.
+  if (process == metrics::CallStackProfileParams::Process::kRenderer &&
+      thread == metrics::CallStackProfileParams::Thread::kMain) {
+    return true;
   }
+
+  // Otherwise enable in dedicated ThreadProfiler browser tests.
+  return browser_test_mode_enabled();
 }
 
 bool AndroidPlatformConfiguration::IsSupportedForChannel(
diff --git a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
index b7d2d44..1520e8d 100644
--- a/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
+++ b/chrome/common/profiler/thread_profiler_platform_configuration_unittest.cc
@@ -151,8 +151,8 @@
 #if BUILDFLAG(IS_ANDROID)
   EXPECT_EQ(0.0, config()->GetChildProcessEnableFraction(
                      metrics::CallStackProfileParams::Process::kGpu));
-  EXPECT_EQ(0.75, config()->GetChildProcessEnableFraction(
-                      metrics::CallStackProfileParams::Process::kRenderer));
+  EXPECT_EQ(0.4, config()->GetChildProcessEnableFraction(
+                     metrics::CallStackProfileParams::Process::kRenderer));
   EXPECT_EQ(0.0,
             config()->GetChildProcessEnableFraction(
                 metrics::CallStackProfileParams::Process::kNetworkService));
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f51a46b..39523f2 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2165,6 +2165,7 @@
       "../browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc",
       "../browser/ui/views/web_apps/frame_toolbar/web_app_minimal_ui_test.cc",
       "../browser/ui/views/web_apps/protocol_handler_launch_dialog_browsertest.cc",
+      "../browser/ui/views/web_apps/web_app_detailed_install_dialog_browsertest.cc",
       "../browser/ui/views/web_apps/web_app_tab_strip_browsertest.cc",
       "../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc",
       "../browser/ui/views/webid/fake_delegate.cc",
@@ -3180,6 +3181,7 @@
         "../browser/ui/views/bubble/webui_bubble_manager_browsertest.cc",
         "../browser/ui/views/certificate_selector_dialog_browsertest.cc",
         "../browser/ui/views/collected_cookies_views_browsertest.cc",
+        "../browser/ui/views/commerce/ntp_discount_consent_dialog_view_browsertest.cc",
         "../browser/ui/views/desktop_capture/desktop_media_picker_views_browsertest.cc",
         "../browser/ui/views/extensions/blocked_action_dialog_view_browsertest.cc",
         "../browser/ui/views/extensions/extension_install_blocked_dialog_view_browsertest.cc",
diff --git a/chrome/test/base/chrome_test_suite.cc b/chrome/test/base/chrome_test_suite.cc
index b4e8b5e..588c96f 100644
--- a/chrome/test/base/chrome_test_suite.cc
+++ b/chrome/test/base/chrome_test_suite.cc
@@ -117,14 +117,17 @@
   // specific path doesn't matter as long as it exists.
   CHECK(scoped_temp_dir_.CreateUniqueTempDir());
   base::FilePath temp_path = scoped_temp_dir_.GetPath();
-  chrome::SetLacrosDefaultPaths(/*documents_dir=*/temp_path,
-                                /*downloads_dir=*/temp_path,
-                                /*drivefs=*/base::FilePath(),
-                                /*removable_media_dir=*/base::FilePath(),
-                                /*android_files_dir=*/base::FilePath(),
-                                /*linux_files_dir=*/base::FilePath(),
-                                /*ash_resources_dir=*/base::FilePath(),
-                                /*share_cache_dir=*/temp_path);
+  chrome::SetLacrosDefaultPaths(
+      /*documents_dir=*/temp_path,
+      /*downloads_dir=*/temp_path,
+      /*drivefs=*/base::FilePath(),
+      /*removable_media_dir=*/base::FilePath(),
+      /*android_files_dir=*/base::FilePath(),
+      /*linux_files_dir=*/base::FilePath(),
+      /*ash_resources_dir=*/base::FilePath(),
+      /*share_cache_dir=*/temp_path,
+      /*preinstalled_web_app_config_dir=*/base::FilePath(),
+      /*preinstalled_web_app_extra_config_dir=*/base::FilePath());
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 5c5e1d4..ec659c51 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -119,6 +119,10 @@
 #include "ui/events/test/event_generator.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if defined(USE_OZONE)
+#include "ui/views/test/test_desktop_screen_ozone.h"
+#endif
+
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/tabs/tab.h"
@@ -595,6 +599,22 @@
   return true;
 }
 
+void InProcessBrowserTest::SetScreenInstance() {
+  // TODO(crbug.com/1317416): On wayland platform, we need to check if the
+  // wayland-ozone platform is initialized at this point due to the async
+  // initialization of the display. Investigate if we can eliminate
+  // IsOzoneInitialized.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (!display::Screen::HasScreen() &&
+      views::test::TestDesktopScreenOzone::IsOzoneInitialized()) {
+    // This is necessary for interactive UI tests.
+    // It is enabled in interactive_ui_tests_main.cc
+    // (or through GPUMain)
+    screen_ = views::test::TestDesktopScreenOzone::Create();
+  }
+#endif
+}
+
 #if !BUILDFLAG(IS_MAC)
 void InProcessBrowserTest::OpenDevToolsWindow(
     content::WebContents* web_contents) {
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index b9ac30b..e154efc 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -52,6 +52,10 @@
 }
 #endif  // defined(TOOLKIT_VIEWS)
 
+namespace display {
+class Screen;
+}
+
 class Browser;
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 class FakeAccountManagerUI;
@@ -225,7 +229,7 @@
   [[nodiscard]] virtual bool SetUpUserDataDirectory();
 
   // Initializes the display::Screen instance.
-  virtual void SetScreenInstance() {}
+  virtual void SetScreenInstance();
 
   // BrowserTestBase:
   void PreRunTestOnMainThread() override;
@@ -314,6 +318,10 @@
   FakeAccountManagerUI* GetFakeAccountManagerUI() const;
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  std::unique_ptr<display::Screen> screen_;
+#endif
+
  private:
   void Initialize();
 
diff --git a/chrome/test/data/capability_delegation/fullscreen_request_delegation_initiator.html b/chrome/test/data/capability_delegation/fullscreen_request_delegation_initiator.html
new file mode 100644
index 0000000..ebe7a3f
--- /dev/null
+++ b/chrome/test/data/capability_delegation/fullscreen_request_delegation_initiator.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+Copyright 2022 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <title>Fullscreen request delegation initiator</title>
+</head>
+<script>
+  // https://wicg.github.io/capability-delegation/spec.html
+  async function delegateCapability(target, origin, capability) {
+    const promise = new Promise(resolve => {
+      window.addEventListener("message", e => resolve(e.data), /*once=*/true);
+    });
+    target.postMessage("", { targetOrigin: origin, delegate: capability });
+    return promise;
+  }
+</script>
+</html>
diff --git a/chrome/test/data/capability_delegation/fullscreen_request_delegation_receiver.html b/chrome/test/data/capability_delegation/fullscreen_request_delegation_receiver.html
new file mode 100644
index 0000000..7998d5c7
--- /dev/null
+++ b/chrome/test/data/capability_delegation/fullscreen_request_delegation_receiver.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+Copyright 2022 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <title>Fullscreen request delegation receiver</title>
+</head>
+<script>
+  // https://wicg.github.io/capability-delegation/spec.html
+  async function requestFullscreenAndReport(event) {
+    document.documentElement.requestFullscreen().finally(() => {
+      event.source.postMessage(!!document.fullscreenElement, "*");
+    });
+  }
+
+  window.addEventListener("message", requestFullscreenAndReport);
+</script>
+</html>
diff --git a/chrome/test/data/extensions/api_test/favicon/extension/test.js b/chrome/test/data/extensions/api_test/favicon/extension/test.js
index 04bf6db..7eec4aba 100644
--- a/chrome/test/data/extensions/api_test/favicon/extension/test.js
+++ b/chrome/test/data/extensions/api_test/favicon/extension/test.js
@@ -84,6 +84,46 @@
         }));
       });
       Promise.all(promises).then(() => chrome.test.succeed());
-    }
+    },
+
+    // Verify that the requested icon size is returned.
+    async function cachedResolutions() {
+      const port = (await chrome.test.getConfig()).testServer.port;
+      const pixelsArr = [16, 32, 64, 48];
+      const promises = pixelsArr.map(pixels => {
+        return new Promise((resolve, reject) => {
+          const favicon = new Favicon({
+            pageUrl: `http://www.example.com:${
+                port}/extensions/favicon/test_file.html`,
+            size: pixels
+          });
+          const image = new Image();
+          image.src = favicon.getUrl();
+          image.onload = () => {
+            chrome.test.assertEq(pixels, image.height);
+            chrome.test.assertEq(pixels, image.width);
+            resolve();
+          }
+        });
+      });
+      Promise.all(promises).then(() => chrome.test.succeed());
+    },
+
+    // The default favicon size is 16.
+    async function defaultSize() {
+      const port = (await chrome.test.getConfig()).testServer.port;
+      const expected = 16;
+      const favicon = new Favicon({
+        pageUrl:
+            `http://www.example.com:${port}/extensions/favicon/test_file.html`
+      });
+      const image = new Image();
+      image.src = favicon.getUrl();
+      image.onload = () => {
+        chrome.test.assertEq(expected, image.height);
+        chrome.test.assertEq(expected, image.width);
+        chrome.test.succeed();
+      }
+    },
   ]);
 }
diff --git a/chrome/test/data/system_extensions/window_manager_extension/manifest.json b/chrome/test/data/system_extensions/window_manager_extension/manifest.json
new file mode 100644
index 0000000..8b889983
--- /dev/null
+++ b/chrome/test/data/system_extensions/window_manager_extension/manifest.json
@@ -0,0 +1,7 @@
+{
+    "name": "Window Manager Extension",
+    "short_name": "WM Extension",
+    "service_worker_url": "/sw.js",
+    "id": "01020304",
+    "type": "echo"
+}
diff --git a/chrome/test/data/system_extensions/window_manager_extension/sw.js b/chrome/test/data/system_extensions/window_manager_extension/sw.js
new file mode 100644
index 0000000..bf96b55
--- /dev/null
+++ b/chrome/test/data/system_extensions/window_manager_extension/sw.js
@@ -0,0 +1,8 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+
+chromeos.windowManagement.addEventListener('start', e => {
+  console.log('start event fired');
+});
diff --git a/chrome/test/data/webui/chromeos/personalization_app/avatar_camera_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/avatar_camera_element_test.ts
index d46942d6..4c21969 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/avatar_camera_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/avatar_camera_element_test.ts
@@ -170,6 +170,30 @@
     assertEquals(2, numFrames, '2 frames requested for video');
   });
 
+  test('displays a loading spinner button while capturing frames', async () => {
+    avatarCameraElement =
+        initElement(AvatarCamera, {mode: AvatarCameraMode.VIDEO});
+    await waitAfterNextRender(avatarCameraElement);
+
+    assertEquals(
+        null, avatarCameraElement.shadowRoot?.getElementById('loadingButton'),
+        'no loading button shown yet');
+
+    avatarCameraElement?.shadowRoot?.getElementById('takePhoto')?.click();
+    await mockWebcamUtils.whenCalled('captureFrames');
+
+    const loadingButton = avatarCameraElement.shadowRoot?.getElementById(
+                              'loadingButton') as HTMLButtonElement;
+    assertTrue(!!loadingButton, 'loading button is shown');
+    assertTrue(loadingButton.disabled, 'loading button is disabled');
+
+    await waitAfterNextRender(avatarCameraElement);
+
+    assertEquals(
+        null, avatarCameraElement.shadowRoot?.getElementById('loadingButton'),
+        'loading button hidden again');
+  });
+
   test('calls saveCameraImage with data on confirmPhoto click', async () => {
     avatarCameraElement =
         initElement(AvatarCamera, {mode: AvatarCameraMode.CAMERA});
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index 2833ca4..1b4ac5f 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -682,6 +682,16 @@
     });
   });
 
+  test('SaveLog', () => {
+    const states = [{state: State.kRepairComplete, error: RmadErrorCode.kOk}];
+    service.setStates(states);
+    const expectedSavePath = {'path': 'fake/save/path'};
+    service.setSaveLogResult(expectedSavePath);
+    return service.saveLog().then((res) => {
+      assertEquals(expectedSavePath, res.savePath);
+    });
+  });
+
   test('SetGetPowerwashRequiredResultTrueUpdatesResult', () => {
     service.setGetPowerwashRequiredResult(true);
 
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/wrapup_repair_complete_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/wrapup_repair_complete_page_test.js
index 4401254..ace671dd 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/wrapup_repair_complete_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/wrapup_repair_complete_page_test.js
@@ -327,6 +327,22 @@
     assertTrue(logsDialog.open);
   });
 
+  test('SaveLogButtonSavesTheLog', async () => {
+    const resolver = new PromiseResolver();
+    await initializeRepairCompletePage();
+
+    let callCount = 0;
+    service.saveLog = () => {
+      callCount++;
+      return resolver.promise;
+    };
+
+    await clickButton('#saveLogDialogButton');
+    await flushTasks();
+
+    assertEquals(1, callCount);
+  });
+
   test('BatteryCutButtonDisabledByDefault', async () => {
     await initializeRepairCompletePage();
     const button = component.shadowRoot.querySelector('#batteryCutButton');
diff --git a/chrome/test/data/webui/history/history_metrics_test.ts b/chrome/test/data/webui/history/history_metrics_test.ts
index fc7fa50..0244ad2 100644
--- a/chrome/test/data/webui/history/history_metrics_test.ts
+++ b/chrome/test/data/webui/history/history_metrics_test.ts
@@ -235,4 +235,17 @@
         .querySelector<HTMLElement>('#menuDeleteButton')!.click();
     assertEquals(1, histogram[SyncedTabsHistogram.HIDE_FOR_NOW]);
   });
+
+  test('history-clusters-duration', async () => {
+    await finishSetup([]);
+
+    navigateTo('/journeys', app);
+    await flushTasks();
+
+    navigateTo('/history', app);
+    await flushTasks();
+
+    const args = await testService.whenCalled('recordLongTime');
+    assertEquals(args[0], 'History.Clusters.WebUISessionDuration');
+  });
 });
diff --git a/chrome/test/data/webui/history/test_browser_service.ts b/chrome/test/data/webui/history/test_browser_service.ts
index ad7890d..fb03426 100644
--- a/chrome/test/data/webui/history/test_browser_service.ts
+++ b/chrome/test/data/webui/history/test_browser_service.ts
@@ -30,6 +30,7 @@
       'queryHistory',
       'queryHistoryContinuation',
       'recordHistogram',
+      'recordLongTime',
       'removeVisits',
       'startSignInFlow',
     ]);
@@ -159,6 +160,11 @@
   }
 
   recordTime() {}
+
+  recordLongTime(histogram: string, value: number) {
+    this.methodCalled('recordLongTime', histogram, value);
+  }
+
   removeBookmark() {}
   startSignInFlow() {}
 }
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
index ce36b53..c3ce1e4 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_page_test.js
@@ -64,6 +64,8 @@
     GuestOsBrowserProxyImpl.instance_ = guestOsBrowserProxy;
     PolymerTest.clearBody();
     crostiniPage = document.createElement('settings-crostini-page');
+    crostiniPage.showCrostini = true;
+    crostiniPage.allowCrostini = true;
     document.body.appendChild(crostiniPage);
     testing.Test.disableAnimationsAndTransitions();
   });
@@ -127,6 +129,22 @@
       setCrostiniPrefs(false);
     });
 
+    test('NotSupported', function() {
+      crostiniPage.showCrostini = false;
+      crostiniPage.allowCrostini = false;
+      flush();
+      assertTrue(!!crostiniPage.$$('#enable'));
+      assertFalse(!!crostiniPage.$$('cr-policy-indicator'));
+    });
+
+    test('NotAllowed', function() {
+      crostiniPage.showCrostini = true;
+      crostiniPage.allowCrostini = false;
+      flush();
+      assertTrue(!!crostiniPage.$$('#enable'));
+      assertTrue(!!crostiniPage.$$('cr-policy-indicator'));
+    });
+
     test('Enable', function() {
       const button = crostiniPage.$$('#enable');
       assertTrue(!!button);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
index d2ebffb..5daf360c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
@@ -240,7 +240,6 @@
 
   test('Check settings-crostini-page exists', async () => {
     init();
-    settingsPage.showCrostini = true;
     const idleRender =
         settingsPage.shadowRoot.querySelector('settings-idle-load');
     await idleRender.get();
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 38d6e50d..a9351bb 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2202,6 +2202,9 @@
       <message name="IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_VIDEO" desc="Label for the button to confirm webcam video">
         Use this video
       </message>
+      <message name="IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS" desc="Label to indicate a webcam video capture is in progress">
+        Taking video
+      </message>
       <message name="IDS_PERSONALIZATION_APP_AVATAR_REJECT_PHOTO" desc="Label for the button to reject webcam photo or video">
         Retake
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS.png.sha1
new file mode 100644
index 0000000..ece96d0
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS.png.sha1
@@ -0,0 +1 @@
+0b0ed010b0f9c44dccc6941129ef8c96df797d60
\ No newline at end of file
diff --git a/chromeos/crosapi/cpp/crosapi_constants.cc b/chromeos/crosapi/cpp/crosapi_constants.cc
index b6260ef7..97d0666 100644
--- a/chromeos/crosapi/cpp/crosapi_constants.cc
+++ b/chromeos/crosapi/cpp/crosapi_constants.cc
@@ -18,6 +18,10 @@
 
 // The file name of shared resource file.
 const char kSharedResourcesPackName[] = "resources_for_sharing.rspak";
+const char kSharedChrome100PercentPackName[] =
+    "chrome_100_percent_for_sharing.rspak";
+const char kSharedChrome200PercentPackName[] =
+    "chrome_200_percent_for_sharing.rspak";
 
 // Release channel key in /etc/lsb-release.
 const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK";
diff --git a/chromeos/crosapi/cpp/crosapi_constants.h b/chromeos/crosapi/cpp/crosapi_constants.h
index 68186bd..a7bc33a 100644
--- a/chromeos/crosapi/cpp/crosapi_constants.h
+++ b/chromeos/crosapi/cpp/crosapi_constants.h
@@ -14,6 +14,8 @@
 COMPONENT_EXPORT(CROSAPI) extern const char kLacrosUserDataPath[];
 
 COMPONENT_EXPORT(CROSAPI) extern const char kSharedResourcesPackName[];
+COMPONENT_EXPORT(CROSAPI) extern const char kSharedChrome100PercentPackName[];
+COMPONENT_EXPORT(CROSAPI) extern const char kSharedChrome200PercentPackName[];
 
 COMPONENT_EXPORT(CROSAPI) extern const char kChromeOSReleaseTrack[];
 
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index e553179..0551899 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -567,8 +567,8 @@
 // future compatibility, to avoid assumptions about where on disk the directory
 // is located.
 //
-// Next version: 34
-// Next id: 9
+// Next version: 35
+// Next id: 11
 [Stable]
 struct DefaultPaths {
   // The default (non-configurable) directory for documents. For example,
@@ -606,6 +606,13 @@
   // The default (non-configurable) directory for shared files. For example,
   // /home/chronos/u-<hash>/ShareCache.
   [MinVersion=33] mojo_base.mojom.FilePath? share_cache@8;
+
+  // The directory where default web app configs are stored.
+  [MinVersion=34] mojo_base.mojom.FilePath? preinstalled_web_app_config@9;
+
+  // The directory where additional web app configs are stored.
+  [MinVersion=34] mojo_base.mojom.FilePath?
+      preinstalled_web_app_extra_config@10;
 };
 
 // The device specific data needed in Lacros.
diff --git a/chromeos/dbus/cros_disks/fake_cros_disks_client.cc b/chromeos/dbus/cros_disks/fake_cros_disks_client.cc
index 0cd632cd..263eb394 100644
--- a/chromeos/dbus/cros_disks/fake_cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks/fake_cros_disks_client.cc
@@ -146,13 +146,14 @@
 
   // Remove the dummy mounted directory if it exists.
   if (mounted_paths_.erase(base::FilePath::FromUTF8Unsafe(device_path))) {
-    base::ThreadPool::PostTaskAndReply(
+    base::ThreadPool::PostTask(
         FROM_HERE,
         {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::GetDeletePathRecursivelyCallback(
-            base::FilePath::FromUTF8Unsafe(device_path)),
-        base::BindOnce(std::move(callback), unmount_error_));
+            base::FilePath::FromUTF8Unsafe(device_path),
+            base::OnceCallback<void(bool)>(base::DoNothing())
+                .Then(base::BindOnce(std::move(callback), unmount_error_))));
   } else {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), unmount_error_));
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index e9339d6..ee2a645 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-104-5045.0-1652697047-benchmark-104.0.5070.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-104-5045.0-1652697047-benchmark-104.0.5071.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 997c70a..23d4fe3 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-104-5045.0-1652694334-benchmark-104.0.5070.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-104-5045.0-1652694334-benchmark-104.0.5071.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/cros_healthd/private/cpp/BUILD.gn b/chromeos/services/cros_healthd/private/cpp/BUILD.gn
index 8d213ca..3714d2e 100644
--- a/chromeos/services/cros_healthd/private/cpp/BUILD.gn
+++ b/chromeos/services/cros_healthd/private/cpp/BUILD.gn
@@ -11,14 +11,9 @@
   sources = [
     "data_collector.cc",
     "data_collector.h",
-    "internal_service_factory.cc",
-    "internal_service_factory.h",
-    "internal_service_factory_impl.cc",
-    "internal_service_factory_impl.h",
   ]
   deps = [
     "//base",
-    "//chromeos/services/network_health/public/mojom",
     "//content/public/browser:browser",
     "//ui/events/devices",
     "//ui/events/ozone/evdev:event_device_info",
@@ -35,17 +30,12 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [
-    "data_collector_unittest.cc",
-    "internal_service_factory_impl_unittest.cc",
-  ]
+  sources = [ "data_collector_unittest.cc" ]
   deps = [
     ":cpp",
     "//base/test:test_support",
     "//content/test:test_support",
-    "//mojo/core/embedder",
     "//mojo/public/cpp/bindings",
-    "//testing/gmock",
     "//testing/gtest",
     "//ui/events/devices",
     "//ui/events/devices:test_support",
diff --git a/chromeos/services/cros_healthd/private/cpp/internal_service_factory.cc b/chromeos/services/cros_healthd/private/cpp/internal_service_factory.cc
deleted file mode 100644
index b2a1ff5..0000000
--- a/chromeos/services/cros_healthd/private/cpp/internal_service_factory.cc
+++ /dev/null
@@ -1,21 +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 "chromeos/services/cros_healthd/private/cpp/internal_service_factory.h"
-
-#include "base/no_destructor.h"
-#include "chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h"
-
-namespace chromeos {
-namespace cros_healthd {
-namespace internal {
-
-InternalServiceFactory* InternalServiceFactory::GetInstance() {
-  static base::NoDestructor<InternalServiceFactoryImpl> service_factory;
-  return service_factory.get();
-}
-
-}  // namespace internal
-}  // namespace cros_healthd
-}  // namespace chromeos
diff --git a/chromeos/services/cros_healthd/private/cpp/internal_service_factory.h b/chromeos/services/cros_healthd/private/cpp/internal_service_factory.h
deleted file mode 100644
index aceb24b..0000000
--- a/chromeos/services/cros_healthd/private/cpp/internal_service_factory.h
+++ /dev/null
@@ -1,66 +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 CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_H_
-#define CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_H_
-
-#include <mojo/public/cpp/bindings/pending_receiver.h>
-
-#include "chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom-forward.h"
-#include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h"
-#include "chromeos/services/network_health/public/mojom/network_health.mojom.h"
-
-namespace chromeos {
-namespace cros_healthd {
-namespace internal {
-
-class InternalServiceFactory {
- public:
-  static InternalServiceFactory* GetInstance();
-
-  using NetworkHealthServiceReceiver = mojo::PendingReceiver<
-      chromeos::network_health::mojom::NetworkHealthService>;
-  using BindNetworkHealthServiceCallback =
-      base::RepeatingCallback<void(NetworkHealthServiceReceiver)>;
-
-  using NetworkDiagnosticsRoutinesReceiver = mojo::PendingReceiver<
-      chromeos::network_diagnostics::mojom::NetworkDiagnosticsRoutines>;
-  using BindNetworkDiagnosticsRoutinesCallback =
-      base::RepeatingCallback<void(NetworkDiagnosticsRoutinesReceiver)>;
-
-  // Sets a callback to request binding a PendingReceiver to the
-  // NetworkHealthService. This callback is invoked when cros_healthd connects.
-  virtual void SetBindNetworkHealthServiceCallback(
-      BindNetworkHealthServiceCallback callback) = 0;
-  // Sets a callback to request binding a PendingReceiver to the
-  // NetworkDiagnosticsRoutines interface. This callback is invoked when
-  // cros_healthd connects.
-  virtual void SetBindNetworkDiagnosticsRoutinesCallback(
-      BindNetworkDiagnosticsRoutinesCallback callback) = 0;
-  // Binds a mojo pending receiver to this service.
-  virtual void BindReceiver(
-      mojo::PendingReceiver<mojom::CrosHealthdInternalServiceFactory>
-          receiver) = 0;
-
- protected:
-  InternalServiceFactory() = default;
-  InternalServiceFactory(const InternalServiceFactory&) = delete;
-  InternalServiceFactory& operator=(const InternalServiceFactory&) = delete;
-  virtual ~InternalServiceFactory() = default;
-};
-
-}  // namespace internal
-}  // namespace cros_healthd
-}  // namespace chromeos
-
-// TODO(https://crbug.com/1164001): remove when moved to ash.
-namespace ash {
-namespace cros_healthd {
-namespace internal {
-using ::chromeos::cros_healthd::internal::InternalServiceFactory;
-}
-}  // namespace cros_healthd
-}  // namespace ash
-
-#endif  // CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_H_
diff --git a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.cc b/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.cc
deleted file mode 100644
index 74374b5..0000000
--- a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.cc
+++ /dev/null
@@ -1,64 +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 "chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h"
-
-namespace chromeos {
-namespace cros_healthd {
-namespace internal {
-
-InternalServiceFactoryImpl::InternalServiceFactoryImpl() = default;
-
-InternalServiceFactoryImpl::~InternalServiceFactoryImpl() = default;
-
-void InternalServiceFactoryImpl::SetBindNetworkHealthServiceCallback(
-    BindNetworkHealthServiceCallback callback) {
-  DCHECK(!callback.is_null());
-  base::AutoLock lock(lock_);
-  bind_network_health_callback_ = std::move(callback);
-  for (auto& receiver : pending_network_health_receivers_) {
-    bind_network_health_callback_.Run(std::move(receiver));
-  }
-  pending_network_health_receivers_.clear();
-}
-
-void InternalServiceFactoryImpl::SetBindNetworkDiagnosticsRoutinesCallback(
-    BindNetworkDiagnosticsRoutinesCallback callback) {
-  DCHECK(!callback.is_null());
-  base::AutoLock lock(lock_);
-  bind_network_diag_callback_ = std::move(callback);
-  for (auto& receiver : pending_network_diag_receivers_) {
-    bind_network_diag_callback_.Run(std::move(receiver));
-  }
-  pending_network_diag_receivers_.clear();
-}
-
-void InternalServiceFactoryImpl::BindReceiver(
-    mojo::PendingReceiver<mojom::CrosHealthdInternalServiceFactory> receiver) {
-  receiver_set_.Add(/*impl=*/this, std::move(receiver));
-}
-
-void InternalServiceFactoryImpl::GetNetworkHealthService(
-    NetworkHealthServiceReceiver receiver) {
-  base::AutoLock lock(lock_);
-  if (bind_network_health_callback_.is_null()) {
-    pending_network_health_receivers_.push_back(std::move(receiver));
-    return;
-  }
-  bind_network_health_callback_.Run(std::move(receiver));
-}
-
-void InternalServiceFactoryImpl::GetNetworkDiagnosticsRoutines(
-    NetworkDiagnosticsRoutinesReceiver receiver) {
-  base::AutoLock lock(lock_);
-  if (bind_network_diag_callback_.is_null()) {
-    pending_network_diag_receivers_.push_back(std::move(receiver));
-    return;
-  }
-  bind_network_diag_callback_.Run(std::move(receiver));
-}
-
-}  // namespace internal
-}  // namespace cros_healthd
-}  // namespace chromeos
diff --git a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h b/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h
deleted file mode 100644
index c0a1e242..0000000
--- a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h
+++ /dev/null
@@ -1,61 +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 CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_IMPL_H_
-#define CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_IMPL_H_
-
-#include "base/synchronization/lock.h"
-#include "chromeos/services/cros_healthd/private/cpp/internal_service_factory.h"
-#include "chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
-
-namespace chromeos {
-namespace cros_healthd {
-namespace internal {
-
-class InternalServiceFactoryImpl
-    : public InternalServiceFactory,
-      public mojom::CrosHealthdInternalServiceFactory {
- public:
-  InternalServiceFactoryImpl();
-  InternalServiceFactoryImpl(const InternalServiceFactoryImpl&) = delete;
-  InternalServiceFactoryImpl& operator=(const InternalServiceFactoryImpl&) =
-      delete;
-
- public:
-  // InternalServiceFactory overrides.
-  void SetBindNetworkHealthServiceCallback(
-      BindNetworkHealthServiceCallback callback) override;
-  void SetBindNetworkDiagnosticsRoutinesCallback(
-      BindNetworkDiagnosticsRoutinesCallback callback) override;
-  void BindReceiver(
-      mojo::PendingReceiver<mojom::CrosHealthdInternalServiceFactory> receiver)
-      override;
-  // mojom::CrosHealthdInternalServiceFactory overrides.
-  void GetNetworkHealthService(NetworkHealthServiceReceiver receiver) override;
-  void GetNetworkDiagnosticsRoutines(
-      NetworkDiagnosticsRoutinesReceiver receiver) override;
-
- protected:
-  ~InternalServiceFactoryImpl() override;
-
- private:
-  // The receiver set to handle connections.
-  mojo::ReceiverSet<mojom::CrosHealthdInternalServiceFactory> receiver_set_;
-  // Lock for access from main thread and mojo thread.
-  base::Lock lock_;
-  // Repeating callbacks that binds a mojo::PendingRemote and returns it.
-  BindNetworkHealthServiceCallback bind_network_health_callback_;
-  BindNetworkDiagnosticsRoutinesCallback bind_network_diag_callback_;
-  // Vectors to keep the pending receivers before the callbacks are ready.
-  std::vector<NetworkHealthServiceReceiver> pending_network_health_receivers_;
-  std::vector<NetworkDiagnosticsRoutinesReceiver>
-      pending_network_diag_receivers_;
-};
-
-}  // namespace internal
-}  // namespace cros_healthd
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_CROS_HEALTHD_PRIVATE_CPP_INTERNAL_SERVICE_FACTORY_IMPL_H_
diff --git a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl_unittest.cc b/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl_unittest.cc
deleted file mode 100644
index fa8df8d7..0000000
--- a/chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl_unittest.cc
+++ /dev/null
@@ -1,116 +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 "chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h"
-
-#include "base/test/bind.h"
-#include "base/test/task_environment.h"
-#include "chromeos/services/cros_healthd/private/cpp/internal_service_factory_impl.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-namespace cros_healthd {
-namespace internal {
-namespace {
-
-namespace network_health_mojom = ::chromeos::network_health::mojom;
-namespace network_diag_mojom = ::chromeos::network_diagnostics::mojom;
-
-class CrosHealthdInternalServiceFactoryImplTest;
-
-class MockInternalServiceFactoryImpl : public InternalServiceFactoryImpl {
-  using InternalServiceFactoryImpl::InternalServiceFactoryImpl;
-};
-
-class CrosHealthdInternalServiceFactoryImplTest : public testing::Test {
- public:
-  CrosHealthdInternalServiceFactoryImplTest() = default;
-  CrosHealthdInternalServiceFactoryImplTest(
-      const CrosHealthdInternalServiceFactoryImplTest&) = delete;
-  CrosHealthdInternalServiceFactoryImplTest& operator=(
-      const CrosHealthdInternalServiceFactoryImplTest&) = delete;
-
-  void SetUp() override {
-    mock_internal_service_factory_.BindReceiver(
-        internal_service_factory_remote_.BindNewPipeAndPassReceiver());
-    EXPECT_TRUE(internal_service_factory_remote_.is_connected());
-  }
-
-  void Flush() { internal_service_factory_remote_.FlushForTesting(); }
-
-  InternalServiceFactoryImpl* mock_internal_service_factory() {
-    return &mock_internal_service_factory_;
-  }
-
-  mojom::CrosHealthdInternalServiceFactory* internal_service_factory_remote() {
-    return internal_service_factory_remote_.get();
-  }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-
-  MockInternalServiceFactoryImpl mock_internal_service_factory_;
-  mojo::Remote<mojom::CrosHealthdInternalServiceFactory>
-      internal_service_factory_remote_;
-};
-
-TEST_F(CrosHealthdInternalServiceFactoryImplTest, GetNetworkHealthService) {
-  mojo::Remote<network_health_mojom::NetworkHealthService> remote;
-  internal_service_factory_remote()->GetNetworkHealthService(
-      remote.BindNewPipeAndPassReceiver());
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkHealthService(
-      remote.BindNewPipeAndPassReceiver());
-
-  int count = 0;
-  auto callback = base::BindLambdaForTesting(
-      [&](mojo::PendingReceiver<network_health_mojom::NetworkHealthService>
-              receiver) { ++count; });
-  mock_internal_service_factory()->SetBindNetworkHealthServiceCallback(
-      callback);
-  Flush();
-  ASSERT_EQ(count, 2);
-
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkHealthService(
-      remote.BindNewPipeAndPassReceiver());
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkHealthService(
-      remote.BindNewPipeAndPassReceiver());
-  Flush();
-  ASSERT_EQ(count, 4);
-}
-
-TEST_F(CrosHealthdInternalServiceFactoryImplTest, GetNetworkDiagService) {
-  mojo::Remote<network_diag_mojom::NetworkDiagnosticsRoutines> remote;
-  internal_service_factory_remote()->GetNetworkDiagnosticsRoutines(
-      remote.BindNewPipeAndPassReceiver());
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkDiagnosticsRoutines(
-      remote.BindNewPipeAndPassReceiver());
-
-  int count = 0;
-  auto callback = base::BindLambdaForTesting(
-      [&](mojo::PendingReceiver<network_diag_mojom::NetworkDiagnosticsRoutines>
-              receiver) { ++count; });
-  mock_internal_service_factory()->SetBindNetworkDiagnosticsRoutinesCallback(
-      callback);
-  Flush();
-  ASSERT_EQ(count, 2);
-
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkDiagnosticsRoutines(
-      remote.BindNewPipeAndPassReceiver());
-  remote.reset();
-  internal_service_factory_remote()->GetNetworkDiagnosticsRoutines(
-      remote.BindNewPipeAndPassReceiver());
-  Flush();
-  ASSERT_EQ(count, 4);
-}
-
-}  // namespace
-}  // namespace internal
-}  // namespace cros_healthd
-}  // namespace chromeos
diff --git a/chromeos/services/cros_healthd/private/mojom/BUILD.gn b/chromeos/services/cros_healthd/private/mojom/BUILD.gn
index 675fca46..ce983ef 100644
--- a/chromeos/services/cros_healthd/private/mojom/BUILD.gn
+++ b/chromeos/services/cros_healthd/private/mojom/BUILD.gn
@@ -6,9 +6,5 @@
 
 mojom("mojom") {
   sources = [ "cros_healthd_internal.mojom" ]
-  public_deps = [
-    "//chromeos/services/cros_healthd/public/mojom",
-    "//chromeos/services/network_health/public/mojom",
-    "//mojo/public/mojom/base",
-  ]
+  public_deps = [ "//mojo/public/mojom/base" ]
 }
diff --git a/chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom b/chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom
index d41b04f..333a6ad 100644
--- a/chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom
+++ b/chromeos/services/cros_healthd/private/mojom/cros_healthd_internal.mojom
@@ -11,44 +11,6 @@
 
 module chromeos.cros_healthd.internal.mojom;
 
-import "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom";
-import "chromeos/services/network_health/public/mojom/network_diagnostics.mojom";
-import "chromeos/services/network_health/public/mojom/network_health.mojom";
-
-// Interface for Chrome to bootstrap the connection between cros_healthd and
-// Chrome. Chrome uses this interface to obtain cros_healthd services and
-// to provide Chrome services.
-//
-// NextMinVersion: 1, NextIndex: 2
-interface ServiceBootstrap {
-    // Pass a receiver of service factory to cros_healthd to bind the services.
-    GetCrosHealthdServiceFactory@0(pending_receiver<
-      chromeos.cros_healthd.mojom.CrosHealthdServiceFactory> receiver);
-    // Pass a remote of internal service factory to cros_healthd for it to
-    // obtain the services in Chrome. This should be the only interface which
-    // the connection is passed from the provider side. All the services in
-    // Chrome should use internal service factory interface to provide services
-    // to cros_healthd.
-    // Should be called only once per connection.
-    SetCrosHealthdInternalServiceFactory@1(pending_remote<
-      CrosHealthdInternalServiceFactory> remote);
-};
-
-// Factory interface which allows remote ends (cros_healthd) to request
-// implementations of several services in Chrome.
-//
-// NextMinVersion: 1, NextIndex: 2
-interface CrosHealthdInternalServiceFactory {
-  // NetworkHealthService provides network health information from chrome
-  // network stack.
-  GetNetworkHealthService@0(pending_receiver<
-      chromeos.network_health.mojom.NetworkHealthService> receiver);
-  // NetworkDiagnosticsRoutines provides network diagnostics routines to test
-  // network.
-  GetNetworkDiagnosticsRoutines@1(pending_receiver<
-      chromeos.network_diagnostics.mojom.NetworkDiagnosticsRoutines> receiver);
-};
-
 // Collects data from chromium to Healthd.
 //
 // NextMinVersion: 1, NextIndex: 2
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc
index ce8a8fc4..476fcad 100644
--- a/components/autofill/core/common/autofill_payments_features.cc
+++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -80,7 +80,7 @@
 // preflight call.
 const base::Feature kAutofillEnableSendingBcnInGetUploadDetails{
     "AutofillEnableSendingBcnInGetUploadDetails",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // When enabled, if the user interacts with the manual fallback bottom sheet
 // on Android, it'll remain sticky until the user dismisses it.
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer.cc b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
index 884f44d..25b260f8 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer.cc
@@ -21,7 +21,6 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "base/version.h"
-#include "components/certificate_transparency/ct_features.h"
 #include "components/certificate_transparency/ct_known_logs.h"
 #include "crypto/sha2.h"
 #include "net/cert/ct_policy_status.h"
@@ -246,14 +245,12 @@
   // months.
   // Increases the SCT requirements for certificates with a lifetime between
   // 180 days and 15 months, from 2 to 3.
+  // This conditional, and the pre-2022 policy logic can be removed after June
+  // 1, 2023, since all publicly trusted certificates issued prior to the
+  // policy change date will have expired by then.
   const base::Time kPolicyUpdateDate =
       base::Time::UnixEpoch() + base::Seconds(1649980800);
-  bool use_2022_policy =
-      base::FeatureList::IsEnabled(
-          features::kCertificateTransparency2022PolicyAllCerts) ||
-      (base::FeatureList::IsEnabled(
-           features::kCertificateTransparency2022Policy) &&
-       issuance_date >= kPolicyUpdateDate);
+  bool use_2022_policy = issuance_date >= kPolicyUpdateDate;
 
   bool has_valid_google_sct = false;
   bool has_valid_nongoogle_sct = false;
@@ -374,18 +371,7 @@
     // AND there is at least one embedded SCT from a non-Google Log once or
     //   currently qualified;
     // ...
-    //
-    // Note: This policy language is only enforced after the below issuance
-    // date, as that's when the diversity policy first came into effect for
-    // SCTs embedded in certificates.
-    // The date when diverse SCTs requirement is effective from.
-    // 2015-07-01 00:00:00 UTC.
-    // TODO(carlosil): There are no more valid certificates from before this
-    // date, so this date and the associated logic should be cleaned up.
-    const base::Time kDiverseSCTRequirementStartDate =
-        base::Time::UnixEpoch() + base::Seconds(1435708800);
-    if (issuance_date >= kDiverseSCTRequirementStartDate &&
-        !(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
+    if (!(has_embedded_google_sct && has_embedded_nongoogle_sct)) {
       // Note: This also covers the case for non-embedded SCTs, as it's only
       // possible to reach here if both sets are not diverse enough.
       return CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS;
diff --git a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
index eded3675..056ceee 100644
--- a/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
+++ b/components/certificate_transparency/chrome_ct_policy_enforcer_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/time.h"
 #include "base/version.h"
-#include "components/certificate_transparency/ct_features.h"
 #include "components/certificate_transparency/ct_known_logs.h"
 #include "crypto/rsa_private_key.h"
 #include "crypto/sha2.h"
@@ -70,7 +69,7 @@
       SignedCertificateTimestamp::Origin desired_origin,
       size_t num_scts,
       const std::vector<std::string>& desired_log_keys,
-      bool timestamp_past_enforcement_date,
+      bool timestamp_past_2022_policy_date,
       SCTList* verified_scts) {
     for (size_t i = 0; i < num_scts; ++i) {
       scoped_refptr<SignedCertificateTimestamp> sct(
@@ -81,11 +80,11 @@
       else
         sct->log_id = std::string(crypto::kSHA256Length, static_cast<char>(i));
 
-      if (timestamp_past_enforcement_date) {
-        EXPECT_TRUE(base::Time::FromUTCExploded({2015, 8, 0, 15, 0, 0, 0, 0},
+      if (timestamp_past_2022_policy_date) {
+        EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 15, 0, 0, 0, 0},
                                                 &sct->timestamp));
       } else {
-        EXPECT_TRUE(base::Time::FromUTCExploded({2015, 6, 0, 15, 0, 0, 0, 0},
+        EXPECT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 14, 0, 0, 0, 0},
                                                 &sct->timestamp));
       }
 
@@ -95,6 +94,7 @@
 
   void AddDisqualifiedLogSCT(SignedCertificateTimestamp::Origin desired_origin,
                              bool timestamp_after_disqualification_date,
+                             bool timestamp_past_2022_policy_date,
                              SCTList* verified_scts) {
     static const char kTestRetiredLogID[] =
         "\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e"
@@ -102,8 +102,14 @@
     static_assert(std::size(kTestRetiredLogID) - 1 == crypto::kSHA256Length,
                   "Incorrect log ID length.");
     base::Time retirement_time;
-    ASSERT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 15, 0, 0, 0, 0},
-                                            &retirement_time));
+    if (timestamp_past_2022_policy_date) {
+      ASSERT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 16, 0, 0, 0, 0},
+                                              &retirement_time));
+    } else {
+      ASSERT_TRUE(base::Time::FromUTCExploded({2022, 4, 0, 14, 12, 0, 0, 0},
+                                              &retirement_time));
+    }
+
     policy_enforcer_->SetDisqualifiedLogForTesting(
         std::make_pair(std::string(kTestRetiredLogID, 32), retirement_time));
 
@@ -112,11 +118,9 @@
     sct->origin = desired_origin;
     sct->log_id = std::string(kTestRetiredLogID, crypto::kSHA256Length);
     if (timestamp_after_disqualification_date) {
-      EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 16, 0, 0, 0, 0},
-                                              &sct->timestamp));
+      sct->timestamp = retirement_time + base::Hours(1);
     } else {
-      EXPECT_TRUE(base::Time::FromUTCExploded({2016, 4, 0, 1, 0, 0, 0, 0},
-                                              &sct->timestamp));
+      sct->timestamp = retirement_time - base::Hours(1);
     }
 
     verified_scts->push_back(sct);
@@ -125,11 +129,12 @@
   void FillListWithSCTsOfOrigin(
       SignedCertificateTimestamp::Origin desired_origin,
       size_t num_scts,
+      bool timestamp_past_2022_policy_date,
       SCTList* verified_scts) {
     std::vector<std::string> desired_log_ids;
     desired_log_ids.push_back(google_log_id_);
-    FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids, true,
-                             verified_scts);
+    FillListWithSCTsOfOrigin(desired_origin, num_scts, desired_log_ids,
+                             timestamp_past_2022_policy_date, verified_scts);
   }
 
   base::Time CreateTime(const base::Time::Exploded& exploded) {
@@ -159,28 +164,13 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Subclass of ChromeCTPolicyEnforcerTest that tests using only the pre-2022
-// policy.
-class ChromeCTPolicyEnforcerTestPre2022Policy
-    : public ChromeCTPolicyEnforcerTest {
- public:
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features*/ {},
-        /*disabled_features*/ {
-            features::kCertificateTransparency2022Policy,
-            features::kCertificateTransparency2022PolicyAllCerts});
-    ChromeCTPolicyEnforcerTest::SetUp();
-  }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllGoogle) {
   SCTList scts;
   std::vector<std::string> desired_log_ids(2, google_log_id_);
 
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           desired_log_ids.size(), desired_log_ids, true,
+                           desired_log_ids.size(), desired_log_ids, false,
                            &scts);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
@@ -188,13 +178,13 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformToCTPolicyNotEnoughDiverseSCTsAllNonGoogle) {
   SCTList scts;
   std::vector<std::string> desired_log_ids(2, non_google_log_id_);
 
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           desired_log_ids.size(), desired_log_ids, true,
+                           desired_log_ids.size(), desired_log_ids, false,
                            &scts);
 
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
@@ -202,20 +192,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
-       ConformsToCTPolicyIfSCTBeforeEnforcementDate) {
-  SCTList scts;
-  // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
-  // All 5 SCTs will be from non-Google logs.
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
-                           std::vector<std::string>(), false, &scts);
-
-  EXPECT_EQ(CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
-            policy_enforcer_->CheckCompliance(chain_.get(), scts,
-                                              NetLogWithSource()));
-}
-
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
        ConformsToPolicyExactNumberOfSCTsForValidityPeriod) {
   std::unique_ptr<crypto::RSAPrivateKey> private_key(
       crypto::RSAPrivateKey::Create(1024));
@@ -284,18 +261,31 @@
 
     for (size_t j = 0; j < required_scts - 1; ++j) {
       SCTList scts;
-      FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, j,
-                               std::vector<std::string>(), false, &scts);
-      EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS,
-                policy_enforcer_->CheckCompliance(cert.get(), scts,
-                                                  NetLogWithSource()))
+      std::vector<std::string> desired_logs;
+      desired_logs.push_back(google_log_id_);
+      if (j > 0) {
+        FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+                                 desired_logs, false, &scts);
+        FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                                 j - 1, std::vector<std::string>(), false,
+                                 &scts);
+      }
+      CTPolicyCompliance expected_failure =
+          j == 1 ? CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS
+                 : CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS;
+      EXPECT_EQ(expected_failure, policy_enforcer_->CheckCompliance(
+                                      cert.get(), scts, NetLogWithSource()))
           << " for: " << (end - start).InDays() << " and " << required_scts
           << " scts=" << scts.size() << " j=" << j;
     }
     SCTList scts;
+    std::vector<std::string> desired_logs;
+    desired_logs.push_back(google_log_id_);
+    FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+                             desired_logs, false, &scts);
     FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                             required_scts, std::vector<std::string>(), false,
-                             &scts);
+                             required_scts - 1, std::vector<std::string>(),
+                             false, &scts);
     EXPECT_EQ(
         CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS,
         policy_enforcer_->CheckCompliance(cert.get(), scts, NetLogWithSource()))
@@ -304,7 +294,7 @@
   }
 }
 
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformToCTPolicyNotEnoughUniqueEmbeddedLogs) {
   SCTList scts;
   std::vector<std::string> desired_logs;
@@ -313,21 +303,21 @@
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, false, &scts);
 
   // Two distinct non-Google logs.
   desired_logs.clear();
   desired_logs.emplace_back(crypto::kSHA256Length, 'A');
   desired_logs.emplace_back(crypto::kSHA256Length, 'B');
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, false, &scts);
 
   // Two unique SCTs from the same non-Google log.
   desired_logs.clear();
   desired_logs.emplace_back(crypto::kSHA256Length, 'C');
   desired_logs.emplace_back(crypto::kSHA256Length, 'C');
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, false, &scts);
 
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
   // However, there are only 4 SCTs are from distinct logs.
@@ -336,8 +326,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTestPre2022Policy,
-       DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToCTPolicyNotEnoughFreshSCTs) {
   SCTList scts;
 
   // The results should be the same before and after disqualification,
@@ -346,9 +335,9 @@
   // SCT from before disqualification.
   scts.clear();
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
+                           1, false, &scts);
   AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                        false, &scts);
+                        false, false, &scts);
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
@@ -356,9 +345,9 @@
   // SCT from after disqualification.
   scts.clear();
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
+                           1, false, &scts);
   AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                        true, &scts);
+                        true, false, &scts);
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
@@ -366,8 +355,9 @@
   // Embedded SCT from before disqualification.
   scts.clear();
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+                           1, false, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, false,
+                        &scts);
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
@@ -375,8 +365,9 @@
   // Embedded SCT from after disqualification.
   scts.clear();
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+                           1, false, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, false,
+                        &scts);
   EXPECT_EQ(CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS,
             policy_enforcer_->CheckCompliance(chain_.get(), scts,
                                               NetLogWithSource()));
@@ -386,28 +377,13 @@
 // policy and the 2022 policy.
 class ChromeCTPolicyEnforcerTestBothPolicies
     : public ChromeCTPolicyEnforcerTest,
-      public ::testing::WithParamInterface<bool> {
- public:
-  void SetUp() override {
-    if (GetParam()) {
-      scoped_feature_list_.InitAndEnableFeature(
-          features::kCertificateTransparency2022PolicyAllCerts);
-    } else {
-      scoped_feature_list_.InitWithFeatures(
-          /*enabled_features*/ {},
-          /*disabled_features*/ {
-              features::kCertificateTransparency2022Policy,
-              features::kCertificateTransparency2022PolicyAllCerts});
-    }
-    ChromeCTPolicyEnforcerTest::SetUp();
-  }
-};
+      public ::testing::WithParamInterface<bool> {};
 
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        ConformsToCTPolicyWithNonEmbeddedSCTs) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
+                           2, GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -422,7 +398,7 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, EnforcementDisabledByBinaryAge) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
+                           2, GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -444,7 +420,8 @@
        ConformsToCTPolicyWithEmbeddedSCTs) {
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
   SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+                           GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -465,13 +442,15 @@
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, GetParam(),
+                           &scts);
 
   // One non-Google log, delivered via TLS.
   desired_logs.clear();
   desired_logs.push_back(non_google_log_id_);
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, GetParam(),
+                           &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -492,13 +471,15 @@
   desired_logs.clear();
   desired_logs.push_back(google_log_id_);
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, GetParam(),
+                           &scts);
 
   // One non-Google log, delivered via OCSP.
   desired_logs.clear();
   desired_logs.push_back(non_google_log_id_);
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
-                           desired_logs.size(), desired_logs, true, &scts);
+                           desired_logs.size(), desired_logs, GetParam(),
+                           &scts);
 
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
@@ -515,7 +496,8 @@
        DoesNotConformToCTPolicyNotEnoughSCTs) {
   // |chain_| is valid for 10 years - over 121 months - so requires 5 SCTs.
   SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 2,
+                           GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -530,8 +512,15 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        ConformsWithDisqualifiedLogBeforeDisqualificationDate) {
   SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 4, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false, &scts);
+  std::vector<std::string> desired_log_ids;
+  desired_log_ids.push_back(google_log_id_);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 1,
+                           desired_log_ids, GetParam(), &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
+                           GetParam() ? 1 : 3, std::vector<std::string>(),
+                           GetParam(), &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, false,
+                        GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -550,8 +539,9 @@
   // Add required - 1 valid SCTs (with the old policy 5 are required, with the
   // new policy 3).
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           GetParam() ? 2 : 4, &scts);
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+                           GetParam() ? 2 : 4, GetParam(), &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true,
+                        GetParam(), &scts);
   if (GetParam()) {
     std::map<std::string, OperatorHistoryEntry> operator_history;
     FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -567,11 +557,12 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        DoesNotConformWithIssuanceDateAfterDisqualificationDate) {
   SCTList scts;
-  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true, &scts);
+  AddDisqualifiedLogSCT(SignedCertificateTimestamp::SCT_EMBEDDED, true,
+                        GetParam(), &scts);
   // Add required - 1 valid SCTs (with the old policy 5 are required, with the
   // new policy 3).
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED,
-                           GetParam() ? 2 : 4, &scts);
+                           GetParam() ? 2 : 4, GetParam(), &scts);
   // Make sure all SCTs are after the disqualification date.
   for (size_t i = 1; i < scts.size(); ++i)
     scts[i]->timestamp = scts[0]->timestamp;
@@ -591,7 +582,7 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, UpdateCTLogList) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           2, &scts);
+                           2, GetParam(), &scts);
 
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs;
@@ -631,7 +622,7 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies, TimestampUpdates) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
-                           1, &scts);
+                           1, GetParam(), &scts);
 
   // Clear the log list and set the last updated time to more than 10 weeks ago.
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
@@ -715,7 +706,8 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        ConformsWithCTPolicyFutureRetirementDateLogs) {
   SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+                           GetParam(), &scts);
 
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs = {google_log_id_};
@@ -747,7 +739,8 @@
 TEST_P(ChromeCTPolicyEnforcerTestBothPolicies,
        DoesNotConformWithCTPolicyPastRetirementDateLogs) {
   SCTList scts;
-  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5, &scts);
+  FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, 5,
+                           GetParam(), &scts);
 
   std::vector<std::pair<std::string, base::Time>> disqualified_logs;
   std::vector<std::string> operated_by_google_logs = {google_log_id_};
@@ -781,17 +774,7 @@
                          ChromeCTPolicyEnforcerTestBothPolicies,
                          testing::Bool());
 
-class ChromeCTPolicyEnforcerTest2022Policy : public ChromeCTPolicyEnforcerTest {
- public:
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kCertificateTransparency2022Policy);
-    ChromeCTPolicyEnforcerTest::SetUp();
-  }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
-       2022PolicyNotInEffectBeforeTargetDate) {
+TEST_F(ChromeCTPolicyEnforcerTest, 2022PolicyNotInEffectBeforeTargetDate) {
   // Old policy should enforce one Google log requirement.
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
@@ -810,8 +793,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022Policy,
-       2022PolicyInEffectAfterTargetDate) {
+TEST_F(ChromeCTPolicyEnforcerTest, 2022PolicyInEffectAfterTargetDate) {
   // New policy should allow SCTs from all non-Google operators to comply as
   // long as diversity requirement is fulfilled.
   SCTList scts;
@@ -831,17 +813,7 @@
                                               NetLogWithSource()));
 }
 
-class ChromeCTPolicyEnforcerTest2022PolicyAllCerts
-    : public ChromeCTPolicyEnforcerTest {
- public:
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kCertificateTransparency2022PolicyAllCerts);
-    ChromeCTPolicyEnforcerTest::SetUp();
-  }
-};
-
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, UpdatedSCTRequirements) {
+TEST_F(ChromeCTPolicyEnforcerTest, UpdatedSCTRequirements) {
   std::unique_ptr<crypto::RSAPrivateKey> private_key(
       crypto::RSAPrivateKey::Create(1024));
   ASSERT_TRUE(private_key);
@@ -896,7 +868,7 @@
     for (size_t j = 0; j <= scts_required; ++j) {
       SCTList scts;
       FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_EMBEDDED, j,
-                               std::vector<std::string>(), false, &scts);
+                               std::vector<std::string>(), true, &scts);
       // Add different operators to the logs so the SCTs comply with operator
       // diversity.
       FillOperatorHistoryWithDiverseOperators(scts, &operator_history);
@@ -921,7 +893,7 @@
   }
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
+TEST_F(ChromeCTPolicyEnforcerTest,
        DoesNotConformToCTPolicyAllLogsSameOperator) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
@@ -939,8 +911,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
-       ConformsToCTPolicyDifferentOperators) {
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToCTPolicyDifferentOperators) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -953,8 +924,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
-       ConformsToPolicyDueToOperatorSwitch) {
+TEST_F(ChromeCTPolicyEnforcerTest, ConformsToPolicyDueToOperatorSwitch) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -976,8 +946,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
-       DoesNotConformToPolicyDueToOperatorSwitch) {
+TEST_F(ChromeCTPolicyEnforcerTest, DoesNotConformToPolicyDueToOperatorSwitch) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -996,7 +965,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts, MultipleOperatorSwitches) {
+TEST_F(ChromeCTPolicyEnforcerTest, MultipleOperatorSwitches) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
@@ -1016,8 +985,7 @@
                                               NetLogWithSource()));
 }
 
-TEST_F(ChromeCTPolicyEnforcerTest2022PolicyAllCerts,
-       MultipleOperatorSwitchesBeforeSCTTimestamp) {
+TEST_F(ChromeCTPolicyEnforcerTest, MultipleOperatorSwitchesBeforeSCTTimestamp) {
   SCTList scts;
   FillListWithSCTsOfOrigin(SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
                            2, std::vector<std::string>(), true, &scts);
diff --git a/components/certificate_transparency/ct_features.cc b/components/certificate_transparency/ct_features.cc
index fea5eea..bb5133d7 100644
--- a/components/certificate_transparency/ct_features.cc
+++ b/components/certificate_transparency/ct_features.cc
@@ -13,12 +13,5 @@
     "CertificateTransparencyComponentUpdater",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kCertificateTransparency2022Policy{
-    "CertificateTransparency2022Policy", base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kCertificateTransparency2022PolicyAllCerts{
-    "CertificateTransparency2022PolicyAllCerts",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 }  // namespace features
 }  // namespace certificate_transparency
diff --git a/components/certificate_transparency/ct_features.h b/components/certificate_transparency/ct_features.h
index 1738fcd..5b20965 100644
--- a/components/certificate_transparency/ct_features.h
+++ b/components/certificate_transparency/ct_features.h
@@ -14,21 +14,6 @@
 COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
 extern const base::Feature kCertificateTransparencyComponentUpdater;
 
-// If enabled, the 2022 CT policy which removes the one Google log
-// requirement, introduces log operator diversity requirements, and increases
-// the number of embedded SCTs required for certificates with a lifetime over
-// 180 days (from 2 to 3) will be used for any certificate issued after February
-// 1, 2022.
-COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
-extern const base::Feature kCertificateTransparency2022Policy;
-
-// If enabled, the 2022 CT policy which removes the one Google log
-// requirement, introduces log operator diversity requirements, and increases
-// the number of embedded SCTs required for certificates with a lifetime over
-// 180 days (from 2 to 3) will be used for all certificates.
-COMPONENT_EXPORT(CERTIFICATE_TRANSPARENCY)
-extern const base::Feature kCertificateTransparency2022PolicyAllCerts;
-
 }  // namespace features
 }  // namespace certificate_transparency
 
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp
index 2528c78..0c9847f 100644
--- a/components/commerce_strings.grdp
+++ b/components/commerce_strings.grdp
@@ -23,7 +23,11 @@
     <message name="IDS_DISCOUNT_CONTEXTUAL_CONSENT_ACCEPTED_CONFIRMATION_DONE" desc="The text shown on the consent confirmation bubble bubble. User can click this button to close the bubble.">
       Done
     </message>
-    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE" translateable="false" desc="The title shown on the native consent dialog for getting discount. Note: This is intentionally left blank. No translation is required."></message>
-    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY" translateable="false" desc="The actual consent descrition that shows in the native consent dialog. Note: This is intentionally left blank. No translation is required."></message>
+    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_TITLE" translateable="false" desc="The title shown on the native consent dialog for getting discount. Note: This is intentionally left blank. No translation is required.">
+      Get discounts on your carts&#13; &amp; when you shop online
+    </message>
+    <message name="IDS_NATIVE_NTP_CART_DISCOUNT_CONSENT_BODY" translateable="false" desc="The actual consent descrition that shows in the native consent dialog. Note: This is intentionally left blank. No translation is required.">
+      Let Google find personalized discounts on your carts and on online stores you visit. When available, discounts will automatically show up on your carts and on sites.
+    </message>
   </if>
 </grit-part>
\ No newline at end of file
diff --git a/components/drive/OWNERS b/components/drive/OWNERS
index 51c60c1..4ad89d4 100644
--- a/components/drive/OWNERS
+++ b/components/drive/OWNERS
@@ -1,7 +1,5 @@
-hashimoto@chromium.org
-hidehiko@chromium.org
-hirono@chromium.org
-kinaba@chromium.org
-slangley@chromium.org
-yoshiki@chromium.org
+# Primary
+file://chrome/browser/sync_file_system/OWNERS
+
+# Secondary
 file://ash/projector/OWNERS
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 7b97fe0..5465a6a 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -155,7 +155,7 @@
         wl_registry_bind(registry, id, &wp_presentation_interface, 1)));
   } else if (strcmp(interface, "zaura_shell") == 0) {
     globals->aura_shell.reset(static_cast<zaura_shell*>(
-        wl_registry_bind(registry, id, &zaura_shell_interface, 14)));
+        wl_registry_bind(registry, id, &zaura_shell_interface, 32)));
   } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
     globals->linux_dmabuf.reset(static_cast<zwp_linux_dmabuf_v1*>(
         wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, 2)));
@@ -923,6 +923,14 @@
     uint32_t modifier_hi,
     uint32_t modifier_lo) {}
 
+////////////////////////////////////////////////////////////////////////////////
+// zaura_output_listener
+
+void ClientBase::HandleInsets(const gfx::Insets& insets) {}
+
+////////////////////////////////////////////////////////////////////////////////
+// helper functions
+
 std::unique_ptr<ClientBase::Buffer> ClientBase::CreateBuffer(
     const gfx::Size& size,
     int32_t drm_format,
@@ -1254,7 +1262,13 @@
       [](void* data, struct zaura_shell* zaura_shell, uint32_t layout_mode) {},
       [](void* data, struct zaura_shell* zaura_shell, uint32_t id) {
         CastToClientBase(data)->bug_fix_ids_.insert(id);
-      }};
+      },
+      [](void* data, struct zaura_shell* zaura_shell,
+         struct wl_array* desk_names) {},
+      [](void* data, struct zaura_shell* zaura_shell,
+         int32_t active_desk_index) {},
+      [](void* data, struct zaura_shell* zaura_shell,
+         struct wl_surface* gained_active, struct wl_surface* lost_active) {}};
   zaura_shell_add_listener(globals_.aura_shell.get(), &kAuraShellListener,
                            this);
 
@@ -1266,6 +1280,23 @@
   }
 
   zaura_surface_set_frame(aura_surface.get(), ZAURA_SURFACE_FRAME_TYPE_NORMAL);
+
+  static zaura_output_listener kAuraOutputListener = {
+      [](void* data, struct zaura_output* zaura_output, uint32_t flags,
+         uint32_t scale) {},
+      [](void* data, struct zaura_output* zaura_output, uint32_t connection) {},
+      [](void* data, struct zaura_output* zaura_output, uint32_t scale) {},
+      [](void* data, struct zaura_output* zaura_output, int32_t top,
+         int32_t left, int32_t bottom, int32_t right) {
+        CastToClientBase(data)->HandleInsets(
+            gfx::Insets::TLBR(top, left, bottom, right));
+      },
+  };
+
+  std::unique_ptr<zaura_output> aura_output(zaura_shell_get_aura_output(
+      globals_.aura_shell.get(), globals_.output.get()));
+  zaura_output_add_listener(aura_output.get(), &kAuraOutputListener, this);
+  globals_.aura_output = std::move(aura_output);
 }
 
 void ClientBase::SetupPointerStylus() {
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h
index b4e47bd..19619be 100644
--- a/components/exo/wayland/clients/client_base.h
+++ b/components/exo/wayland/clients/client_base.h
@@ -15,6 +15,7 @@
 #include "linux-dmabuf-unstable-v1-client-protocol.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_surface.h"
@@ -85,6 +86,7 @@
     std::unique_ptr<wl_subcompositor> subcompositor;
     std::unique_ptr<wl_touch> touch;
     std::unique_ptr<zaura_shell> aura_shell;
+    std::unique_ptr<zaura_output> aura_output;
     std::unique_ptr<zxdg_shell_v6> xdg_shell_v6;
     std::unique_ptr<xdg_wm_base> xdg_wm_base;
     std::unique_ptr<zwp_fullscreen_shell_v1> fullscreen_shell;
@@ -204,6 +206,9 @@
       uint32_t modifier_hi,
       uint32_t modifier_lo);
 
+  // zaura_output_listener
+  virtual void HandleInsets(const gfx::Insets& insets);
+
   gfx::Size size_ = gfx::Size(256, 256);
   int scale_ = 1;
   int transform_ = WL_OUTPUT_TRANSFORM_NORMAL;
diff --git a/components/exo/wayland/clients/info.cc b/components/exo/wayland/clients/info.cc
index 1e97fac3..baf36fd 100644
--- a/components/exo/wayland/clients/info.cc
+++ b/components/exo/wayland/clients/info.cc
@@ -43,6 +43,9 @@
   };
   // |next_scales| are swapped with |scales| after receiving output done event.
   std::vector<Scale> scales, next_scales;
+  struct {
+    int32_t top, left, bottom, right;
+  } insets;
   std::unique_ptr<wl_output> output;
   std::unique_ptr<zaura_output> aura_output;
 };
@@ -155,6 +158,17 @@
   info->device_scale_factor = device_scale_factor;
 }
 
+void AuraOutputInsets(void* data,
+                      zaura_output* output,
+                      int32_t top,
+                      int32_t left,
+                      int32_t bottom,
+                      int32_t right) {
+  Info* info = static_cast<Info*>(data);
+
+  info->insets = {top, left, bottom, right};
+}
+
 std::string OutputSubpixelToString(int32_t subpixel) {
   switch (subpixel) {
     case WL_OUTPUT_SUBPIXEL_UNKNOWN:
@@ -293,7 +307,8 @@
                                         OutputScale};
 
   zaura_output_listener aura_output_listener = {
-      AuraOutputScale, AuraOutputConnection, AuraOutputDeviceScaleFactor};
+      AuraOutputScale, AuraOutputConnection, AuraOutputDeviceScaleFactor,
+      AuraOutputInsets};
   for (auto& info : globals.outputs) {
     wl_output_add_listener(info.output.get(), &output_listener, &info);
     if (globals.aura_shell) {
@@ -349,6 +364,12 @@
                   << AuraOutputScaleFlagsToString(scale.flags) << std::endl;
       }
     }
+    std::cout << "  insets:" << std::endl
+              << "    top:     " << info.insets.top << std::endl
+              << "    left:    " << info.insets.left << std::endl
+              << "    bottom:  " << info.insets.bottom << std::endl
+              << "    right:   " << info.insets.right << std::endl
+              << std::endl;
   }
 
   return 0;
diff --git a/components/exo/wayland/protocol/aura-shell.xml b/components/exo/wayland/protocol/aura-shell.xml
index 8a4bffb..b0fd1b5 100644
--- a/components/exo/wayland/protocol/aura-shell.xml
+++ b/components/exo/wayland/protocol/aura-shell.xml
@@ -24,7 +24,7 @@
     DEALINGS IN THE SOFTWARE.
   </copyright>
 
-  <interface name="zaura_shell" version="31">
+  <interface name="zaura_shell" version="32">
     <description summary="aura_shell">
       The global interface exposing aura shell capabilities is used to
       instantiate an interface extension for a wl_surface object.
@@ -512,7 +512,7 @@
     </event>
   </interface>
 
-  <interface name="zaura_output" version="6">
+  <interface name="zaura_output" version="32">
     <description summary="aura shell interface to a wl_output">
       An additional interface to a wl_output object, which allows the
       client to access aura shell specific functionality for output.
@@ -614,7 +614,23 @@
       </description>
       <arg name="scale" type="uint" enum="scale_factor" summary="output device scale factor"/>
     </event>
- </interface>
+
+    <!-- Version 32 additions -->
+
+    <event name="insets" since="32">
+      <description summary="advertise the insets for the output">
+        This event describes the insets for the output in logical screen
+        coordinates, from which the work area can be calculated.
+
+        This event is sent before wl_output.done, after which the client would
+        apply the change.
+      </description>
+      <arg name="top" type="int"/>
+      <arg name="left" type="int"/>
+      <arg name="bottom" type="int"/>
+      <arg name="right" type="int"/>
+    </event>
+  </interface>
 
   <interface name="zaura_toplevel" version="31">
     <description summary="aura shell interface to the toplevel shell">
diff --git a/components/exo/wayland/zaura_shell.cc b/components/exo/wayland/zaura_shell.cc
index ea481e07..468c7eefd 100644
--- a/components/exo/wayland/zaura_shell.cc
+++ b/components/exo/wayland/zaura_shell.cc
@@ -774,90 +774,88 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-namespace {
-
 ////////////////////////////////////////////////////////////////////////////////
 // aura_output_interface:
 
-class AuraOutput : public WaylandDisplayObserver {
- public:
-  explicit AuraOutput(wl_resource* resource) : resource_(resource) {}
+AuraOutput::AuraOutput(wl_resource* resource) : resource_(resource) {}
 
-  AuraOutput(const AuraOutput&) = delete;
-  AuraOutput& operator=(const AuraOutput&) = delete;
+AuraOutput::~AuraOutput() = default;
 
-  // Overridden from WaylandDisplayObserver:
-  bool SendDisplayMetrics(const display::Display& display,
-                          uint32_t changed_metrics) override {
-    if (!(changed_metrics &
-          (display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
-           display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
-           display::DisplayObserver::DISPLAY_METRIC_ROTATION))) {
-      return false;
-    }
-
-    const WMHelper* wm_helper = WMHelper::GetInstance();
-    const display::ManagedDisplayInfo& display_info =
-        wm_helper->GetDisplayInfo(display.id());
-
-    if (wl_resource_get_version(resource_) >=
-        ZAURA_OUTPUT_SCALE_SINCE_VERSION) {
-      display::ManagedDisplayMode active_mode;
-      bool rv =
-          wm_helper->GetActiveModeForDisplayId(display.id(), &active_mode);
-      DCHECK(rv);
-      const int32_t current_output_scale =
-          std::round(display_info.zoom_factor() * 1000.f);
-      std::vector<float> zoom_factors =
-          display::GetDisplayZoomFactors(active_mode);
-
-      // Ensure that the current zoom factor is a part of the list.
-      auto it = std::find_if(
-          zoom_factors.begin(), zoom_factors.end(),
-          [&display_info](float zoom_factor) -> bool {
-            return std::abs(display_info.zoom_factor() - zoom_factor) <=
-                   std::numeric_limits<float>::epsilon();
-          });
-      if (it == zoom_factors.end())
-        zoom_factors.push_back(display_info.zoom_factor());
-
-      for (float zoom_factor : zoom_factors) {
-        int32_t output_scale = std::round(zoom_factor * 1000.f);
-        uint32_t flags = 0;
-        if (output_scale == 1000)
-          flags |= ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED;
-        if (current_output_scale == output_scale)
-          flags |= ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT;
-
-        // TODO(malaykeshav): This can be removed in the future when client
-        // has been updated.
-        if (wl_resource_get_version(resource_) < 6)
-          output_scale = std::round(1000.f / zoom_factor);
-
-        zaura_output_send_scale(resource_, flags, output_scale);
-      }
-    }
-
-    if (wl_resource_get_version(resource_) >=
-        ZAURA_OUTPUT_CONNECTION_SINCE_VERSION) {
-      zaura_output_send_connection(resource_,
-                                   display.IsInternal()
-                                       ? ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL
-                                       : ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN);
-    }
-
-    if (wl_resource_get_version(resource_) >=
-        ZAURA_OUTPUT_DEVICE_SCALE_FACTOR_SINCE_VERSION) {
-      zaura_output_send_device_scale_factor(
-          resource_, display_info.device_scale_factor() * 1000);
-    }
-
-    return true;
+bool AuraOutput::SendDisplayMetrics(const display::Display& display,
+                                    uint32_t changed_metrics) {
+  if (!(changed_metrics &
+        (display::DisplayObserver::DISPLAY_METRIC_BOUNDS |
+         display::DisplayObserver::DISPLAY_METRIC_WORK_AREA |
+         display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR |
+         display::DisplayObserver::DISPLAY_METRIC_ROTATION))) {
+    return false;
   }
 
- private:
-  wl_resource* const resource_;
-};
+  const WMHelper* wm_helper = WMHelper::GetInstance();
+  const display::ManagedDisplayInfo& display_info =
+      wm_helper->GetDisplayInfo(display.id());
+
+  if (wl_resource_get_version(resource_) >= ZAURA_OUTPUT_SCALE_SINCE_VERSION) {
+    display::ManagedDisplayMode active_mode;
+    bool rv = wm_helper->GetActiveModeForDisplayId(display.id(), &active_mode);
+    DCHECK(rv);
+    const int32_t current_output_scale =
+        std::round(display_info.zoom_factor() * 1000.f);
+    std::vector<float> zoom_factors =
+        display::GetDisplayZoomFactors(active_mode);
+
+    // Ensure that the current zoom factor is a part of the list.
+    auto it = std::find_if(
+        zoom_factors.begin(), zoom_factors.end(),
+        [&display_info](float zoom_factor) -> bool {
+          return std::abs(display_info.zoom_factor() - zoom_factor) <=
+                 std::numeric_limits<float>::epsilon();
+        });
+    if (it == zoom_factors.end())
+      zoom_factors.push_back(display_info.zoom_factor());
+
+    for (float zoom_factor : zoom_factors) {
+      int32_t output_scale = std::round(zoom_factor * 1000.f);
+      uint32_t flags = 0;
+      if (output_scale == 1000)
+        flags |= ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED;
+      if (current_output_scale == output_scale)
+        flags |= ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT;
+
+      // TODO(malaykeshav): This can be removed in the future when client
+      // has been updated.
+      if (wl_resource_get_version(resource_) < 6)
+        output_scale = std::round(1000.f / zoom_factor);
+
+      zaura_output_send_scale(resource_, flags, output_scale);
+    }
+  }
+
+  if (wl_resource_get_version(resource_) >=
+      ZAURA_OUTPUT_CONNECTION_SINCE_VERSION) {
+    zaura_output_send_connection(
+        resource_, display.IsInternal() ? ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL
+                                        : ZAURA_OUTPUT_CONNECTION_TYPE_UNKNOWN);
+  }
+
+  if (wl_resource_get_version(resource_) >=
+      ZAURA_OUTPUT_DEVICE_SCALE_FACTOR_SINCE_VERSION) {
+    zaura_output_send_device_scale_factor(
+        resource_, display_info.device_scale_factor() * 1000);
+  }
+
+  if (wl_resource_get_version(resource_) >= ZAURA_OUTPUT_INSETS_SINCE_VERSION)
+    SendInsets(display.bounds().InsetsFrom(display.work_area()));
+
+  return true;
+}
+
+void AuraOutput::SendInsets(const gfx::Insets& insets) {
+  zaura_output_send_insets(resource_, insets.top(), insets.left(),
+                           insets.bottom(), insets.right());
+}
+
+namespace {
 
 ////////////////////////////////////////////////////////////////////////////////
 // aura_shell_interface:
diff --git a/components/exo/wayland/zaura_shell.h b/components/exo/wayland/zaura_shell.h
index dda9f92..ae7bc45 100644
--- a/components/exo/wayland/zaura_shell.h
+++ b/components/exo/wayland/zaura_shell.h
@@ -10,6 +10,8 @@
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/exo/surface.h"
 #include "components/exo/surface_observer.h"
+#include "components/exo/wayland/wayland_display_observer.h"
+#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/wm/public/activation_change_observer.h"
 
@@ -24,7 +26,7 @@
 namespace wayland {
 class SerialTracker;
 
-constexpr uint32_t kZAuraShellVersion = 31;
+constexpr uint32_t kZAuraShellVersion = 32;
 
 // Adds bindings to the Aura Shell. Normally this implies Ash on ChromeOS
 // builds. On non-ChromeOS builds the protocol provides access to Aura windowing
@@ -148,6 +150,26 @@
   ShellSurfaceBase* shell_surface_;
 };
 
+class AuraOutput : public WaylandDisplayObserver {
+ public:
+  explicit AuraOutput(wl_resource* resource);
+
+  AuraOutput(const AuraOutput&) = delete;
+  AuraOutput& operator=(const AuraOutput&) = delete;
+
+  ~AuraOutput() override;
+
+  // Overridden from WaylandDisplayObserver:
+  bool SendDisplayMetrics(const display::Display& display,
+                          uint32_t changed_metrics) override;
+
+ protected:
+  virtual void SendInsets(const gfx::Insets& insets);
+
+ private:
+  wl_resource* const resource_;
+};
+
 }  // namespace wayland
 }  // namespace exo
 
diff --git a/components/exo/wayland/zaura_shell_unittest.cc b/components/exo/wayland/zaura_shell_unittest.cc
index b5b975f..1ac9993 100644
--- a/components/exo/wayland/zaura_shell_unittest.cc
+++ b/components/exo/wayland/zaura_shell_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <aura-shell-server-protocol.h>
 
+#include <sys/socket.h>
 #include <memory>
 
 #include "ash/session/session_controller_impl.h"
@@ -402,5 +403,40 @@
   aura_surface().SetFullscreenMode(ZAURA_SURFACE_FULLSCREEN_MODE_IMMERSIVE);
 }
 
+class MockAuraOutput : public AuraOutput {
+ public:
+  using AuraOutput::AuraOutput;
+
+  MOCK_METHOD(void, SendInsets, (const gfx::Insets&), (override));
+};
+
+using ZAuraOutputTest = test::ExoTestBase;
+
+TEST_F(ZAuraOutputTest, SendInsets) {
+  int fds[2];
+  ASSERT_EQ(
+      socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, /*protocol=*/0, fds), 0);
+  wl_display* wayland_display = wl_display_create();
+  wl_client* client = wl_client_create(wayland_display, fds[0]);
+  wl_resource* resource = wl_resource_create(
+      client, &zaura_output_interface, ZAURA_OUTPUT_INSETS_SINCE_VERSION, 0);
+
+  MockAuraOutput mock_aura_output(resource);
+
+  UpdateDisplay("800x600");
+  display::Display display =
+      display_manager()->GetDisplayForId(display_manager()->first_display_id());
+  const gfx::Rect initial_bounds{800, 600};
+  EXPECT_EQ(display.bounds(), initial_bounds);
+  const gfx::Rect new_work_area{10, 20, 500, 400};
+  EXPECT_NE(display.work_area(), new_work_area);
+  display.set_work_area(new_work_area);
+
+  const gfx::Insets expected_insets = initial_bounds.InsetsFrom(new_work_area);
+  EXPECT_CALL(mock_aura_output, SendInsets(expected_insets)).Times(1);
+  mock_aura_output.SendDisplayMetrics(
+      display, display::DisplayObserver::DISPLAY_METRIC_WORK_AREA);
+}
+
 }  // namespace wayland
 }  // namespace exo
diff --git a/components/performance_manager/service_worker_context_adapter.cc b/components/performance_manager/service_worker_context_adapter.cc
index 0f88c05..2da5bade 100644
--- a/components/performance_manager/service_worker_context_adapter.cc
+++ b/components/performance_manager/service_worker_context_adapter.cc
@@ -194,6 +194,13 @@
   NOTIMPLEMENTED();
 }
 
+service_manager::InterfaceProvider*
+ServiceWorkerContextAdapter::GetRemoteInterfaces(
+    int64_t service_worker_version_id) {
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 void ServiceWorkerContextAdapter::StartServiceWorkerAndDispatchMessage(
     const GURL& scope,
     const blink::StorageKey& key,
diff --git a/components/performance_manager/service_worker_context_adapter.h b/components/performance_manager/service_worker_context_adapter.h
index b654575..9d4ba7a 100644
--- a/components/performance_manager/service_worker_context_adapter.h
+++ b/components/performance_manager/service_worker_context_adapter.h
@@ -100,6 +100,8 @@
   const base::flat_map<int64_t /* version_id */,
                        content::ServiceWorkerRunningInfo>&
   GetRunningServiceWorkerInfos() override;
+  service_manager::InterfaceProvider* GetRemoteInterfaces(
+      int64_t service_worker_version_id) override;
 
   // content::ServiceWorkerContextObserver:
   void OnRegistrationCompleted(const GURL& scope) override;
diff --git a/components/segmentation_platform/internal/BUILD.gn b/components/segmentation_platform/internal/BUILD.gn
index 3fa77f6..40e7c0e5 100644
--- a/components/segmentation_platform/internal/BUILD.gn
+++ b/components/segmentation_platform/internal/BUILD.gn
@@ -28,8 +28,6 @@
     "database/database_maintenance.h",
     "database/database_maintenance_impl.cc",
     "database/database_maintenance_impl.h",
-    "database/metadata_utils.cc",
-    "database/metadata_utils.h",
     "database/segment_info_database.cc",
     "database/segment_info_database.h",
     "database/signal_database.h",
@@ -84,6 +82,10 @@
     "execution/processing/uma_feature_processor.h",
     "local_state_helper_impl.cc",
     "local_state_helper_impl.h",
+    "metadata/metadata_utils.cc",
+    "metadata/metadata_utils.h",
+    "metadata/metadata_writer.cc",
+    "metadata/metadata_writer.h",
     "metric_filter_utils.cc",
     "metric_filter_utils.h",
     "platform_options.cc",
@@ -194,7 +196,6 @@
   sources = [
     "data_collection/training_data_collector_impl_unittest.cc",
     "database/database_maintenance_impl_unittest.cc",
-    "database/metadata_utils_unittest.cc",
     "database/mock_signal_database.cc",
     "database/mock_signal_database.h",
     "database/mock_signal_storage_config.cc",
@@ -226,6 +227,7 @@
     "execution/processing/mock_feature_list_query_processor.h",
     "execution/processing/query_processor.h",
     "execution/processing/sql_feature_processor_unittest.cc",
+    "metadata/metadata_utils_unittest.cc",
     "mock_ukm_data_manager.cc",
     "mock_ukm_data_manager.h",
     "scheduler/model_execution_scheduler_unittest.cc",
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index ac00d464..8298b050 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -9,9 +9,9 @@
 #include "base/notreached.h"
 #include "base/time/clock.h"
 #include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
@@ -235,10 +235,8 @@
   base::TimeDelta signal_storage_length =
       model_metadata.signal_storage_length() *
       metadata_utils::GetTimeUnit(model_metadata);
-  if (LocalStateHelper::GetInstance().GetPrefTime(
-          kSegmentationUkmMostRecentAllowedTimeKey) +
-          signal_storage_length >=
-      clock_->Now()) {
+  if (!SegmentationUkmHelper::AllowedToUploadData(signal_storage_length,
+                                                  clock_)) {
     RecordTrainingDataCollectionEvent(
         segment_info.segment_id(),
         stats::TrainingDataCollectionEvent::kPartialDataNotAllowed);
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
index aa2c800..c2daf82 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl_unittest.cc
@@ -13,10 +13,10 @@
 #include "base/test/task_environment.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/segmentation_ukm_helper.h"
diff --git a/components/segmentation_platform/internal/database/database_maintenance_impl.cc b/components/segmentation_platform/internal/database/database_maintenance_impl.cc
index c81318c4..aa35a1de 100644
--- a/components/segmentation_platform/internal/database/database_maintenance_impl.cc
+++ b/components/segmentation_platform/internal/database/database_maintenance_impl.cc
@@ -20,11 +20,11 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/config.h"
diff --git a/components/segmentation_platform/internal/database/signal_database_impl.cc b/components/segmentation_platform/internal/database/signal_database_impl.cc
index 2d01518..4e8f496 100644
--- a/components/segmentation_platform/internal/database/signal_database_impl.cc
+++ b/components/segmentation_platform/internal/database/signal_database_impl.cc
@@ -18,9 +18,9 @@
 #include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
 #include "components/leveldb_proto/public/proto_database.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_key.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/signal.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/components/segmentation_platform/internal/database/signal_storage_config.cc b/components/segmentation_platform/internal/database/signal_storage_config.cc
index eef23789..e35d2bbd 100644
--- a/components/segmentation_platform/internal/database/signal_storage_config.cc
+++ b/components/segmentation_platform/internal/database/signal_storage_config.cc
@@ -6,7 +6,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 
 namespace segmentation_platform {
 namespace {
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.cc b/components/segmentation_platform/internal/database/test_segment_info_database.cc
index 5f5ff8f..3fbd7c3 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.cc
@@ -10,38 +10,13 @@
 #include "base/metrics/metrics_hashes.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
-#include "components/segmentation_platform/internal/signals/ukm_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform::test {
 
-namespace {
-void AddFeature(proto::SegmentInfo* segment_info,
-                proto::SignalType signal_type,
-                const std::string& name,
-                uint64_t bucket_count,
-                uint64_t tensor_length,
-                proto::Aggregation aggregation,
-                const std::vector<int32_t>& accepted_enum_ids) {
-  proto::SegmentationModelMetadata* metadata =
-      segment_info->mutable_model_metadata();
-  proto::InputFeature* input = metadata->add_input_features();
-  proto::UMAFeature* feature = input->mutable_uma_feature();
-  feature->set_type(signal_type);
-  feature->set_name(name);
-  feature->set_name_hash(base::HashMetricName(name));
-  feature->set_bucket_count(bucket_count);
-  feature->set_tensor_length(tensor_length);
-  feature->set_aggregation(aggregation);
-
-  for (int32_t accepted_enum_id : accepted_enum_ids)
-    feature->add_enum_ids(accepted_enum_id);
-}
-}  // namespace
-
 TestSegmentInfoDatabase::TestSegmentInfoDatabase()
     : SegmentInfoDatabase(nullptr) {}
 
@@ -123,8 +98,16 @@
     uint64_t tensor_length,
     proto::Aggregation aggregation) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
-  AddFeature(info, proto::SignalType::USER_ACTION, name, bucket_count,
-             tensor_length, aggregation, {});
+  MetadataWriter writer(info->mutable_model_metadata());
+  MetadataWriter::UMAFeature feature{
+      .signal_type = proto::SignalType::USER_ACTION,
+      .name = name.c_str(),
+      .bucket_count = bucket_count,
+      .tensor_length = tensor_length,
+      .aggregation = aggregation,
+      .accepted_enum_ids = nullptr};
+  MetadataWriter::UMAFeature features[] = {feature};
+  writer.AddUmaFeatures(features, 1);
 }
 
 void TestSegmentInfoDatabase::AddHistogramValueFeature(
@@ -134,8 +117,16 @@
     uint64_t tensor_length,
     proto::Aggregation aggregation) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
-  AddFeature(info, proto::SignalType::HISTOGRAM_VALUE, name, bucket_count,
-             tensor_length, aggregation, {});
+  MetadataWriter writer(info->mutable_model_metadata());
+  MetadataWriter::UMAFeature feature{
+      .signal_type = proto::SignalType::HISTOGRAM_VALUE,
+      .name = name.c_str(),
+      .bucket_count = bucket_count,
+      .tensor_length = tensor_length,
+      .aggregation = aggregation,
+      .accepted_enum_ids = nullptr};
+  MetadataWriter::UMAFeature features[] = {feature};
+  writer.AddUmaFeatures(features, 1);
 }
 
 void TestSegmentInfoDatabase::AddHistogramEnumFeature(
@@ -146,26 +137,26 @@
     proto::Aggregation aggregation,
     const std::vector<int32_t>& accepted_enum_ids) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
-  AddFeature(info, proto::SignalType::HISTOGRAM_ENUM, name, bucket_count,
-             tensor_length, aggregation, accepted_enum_ids);
+  MetadataWriter writer(info->mutable_model_metadata());
+  MetadataWriter::UMAFeature feature{
+      .signal_type = proto::SignalType::HISTOGRAM_ENUM,
+      .name = name.c_str(),
+      .bucket_count = bucket_count,
+      .tensor_length = tensor_length,
+      .aggregation = aggregation,
+      .enum_ids_size = accepted_enum_ids.size(),
+      .accepted_enum_ids = accepted_enum_ids.data()};
+  MetadataWriter::UMAFeature features[] = {feature};
+  writer.AddUmaFeatures(features, 1);
 }
 
-void TestSegmentInfoDatabase::AddSqlFeature(OptimizationTarget segment_id,
-                                            const std::string& sql,
-                                            const UkmConfig& event_config) {
+void TestSegmentInfoDatabase::AddSqlFeature(
+    OptimizationTarget segment_id,
+    const MetadataWriter::SqlFeature& feature) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
-  auto* metadata = info->mutable_model_metadata();
-  proto::SqlFeature* feature =
-      metadata->add_input_features()->mutable_sql_feature();
-  feature->set_sql(sql);
-  for (const auto& event_it : event_config.metrics_for_event_for_testing()) {
-    auto* ukm_event = feature->mutable_signal_filter()->add_ukm_events();
-    const UkmEventHash event_hash = event_it.first;
-    ukm_event->set_event_hash(event_hash.GetUnsafeValue());
-    const base::flat_set<UkmMetricHash>& metrics = event_it.second;
-    for (const auto& metric : metrics)
-      ukm_event->mutable_metric_hash_filter()->Add(metric.GetUnsafeValue());
-  }
+  MetadataWriter writer(info->mutable_model_metadata());
+  MetadataWriter::SqlFeature features[] = {feature};
+  writer.AddSqlFeatures(features, 1);
 }
 
 void TestSegmentInfoDatabase::AddPredictionResult(OptimizationTarget segment_id,
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.h b/components/segmentation_platform/internal/database/test_segment_info_database.h
index abcd6de..b50d9c41 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.h
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.h
@@ -11,15 +11,12 @@
 #include "base/containers/flat_set.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 
-namespace segmentation_platform {
-
-class UkmConfig;
-
-namespace test {
+namespace segmentation_platform::test {
 
 // A fake database with sample entries that can be used for tests.
 class TestSegmentInfoDatabase : public SegmentInfoDatabase {
@@ -60,8 +57,7 @@
                                proto::Aggregation aggregation,
                                const std::vector<int32_t>& accepted_enum_ids);
   void AddSqlFeature(OptimizationTarget segment_id,
-                     const std::string& sql,
-                     const UkmConfig& ukm_config);
+                     const MetadataWriter::SqlFeature& feature);
   void AddPredictionResult(OptimizationTarget segment_id,
                            float score,
                            base::Time timestamp);
@@ -80,8 +76,6 @@
   std::vector<std::pair<OptimizationTarget, proto::SegmentInfo>> segment_infos_;
 };
 
-}  // namespace test
-
-}  // namespace segmentation_platform
+}  // namespace segmentation_platform::test
 
 #endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_TEST_SEGMENT_INFO_DATABASE_H_
diff --git a/components/segmentation_platform/internal/database/ukm_types.h b/components/segmentation_platform/internal/database/ukm_types.h
index 0dddd25f..5b94a59 100644
--- a/components/segmentation_platform/internal/database/ukm_types.h
+++ b/components/segmentation_platform/internal/database/ukm_types.h
@@ -9,6 +9,7 @@
 
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "base/time/time.h"
 #include "base/types/id_type.h"
 
@@ -21,6 +22,9 @@
 using UkmMetricHash = base::IdTypeU64<class UkmMetricHashTag>;
 using UrlId = base::IdType64<class UrlIdTag>;
 
+using UkmEventsToMetricsMap =
+    base::flat_map<UkmEventHash, base::flat_set<UkmMetricHash>>;
+
 namespace processing {
 
 // A struct that can accommodate multiple output types needed for Segmentation
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
index 5f0bc83..0c58d556 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -17,8 +17,8 @@
 #include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index 14fe745..882d042 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/mock_signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
@@ -27,6 +26,7 @@
 #include "components/segmentation_platform/internal/execution/model_execution_manager.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
diff --git a/components/segmentation_platform/internal/execution/model_executor_impl.cc b/components/segmentation_platform/internal/execution/model_executor_impl.cc
index 79a924a..8db1f8f 100644
--- a/components/segmentation_platform/internal/execution/model_executor_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_executor_impl.cc
@@ -62,6 +62,7 @@
   std::vector<float> input_tensor;
   base::Time total_execution_start_time;
   base::Time model_execution_start_time;
+  base::TimeDelta signal_storage_length;
 };
 
 ModelExecutorImpl::ModelExecutionTraceEvent::ModelExecutionTraceEvent(
@@ -105,7 +106,7 @@
   ModelExecutionTraceEvent trace_event("ModelExecutorImpl::ExecuteModel",
                                        *state);
 
-  if (!state->model_provider->ModelAvailable()) {
+  if (!state->model_provider || !state->model_provider->ModelAvailable()) {
     RunModelExecutionCallback(std::move(state), 0,
                               ModelExecutionStatus::kSkippedModelNotReady);
     return;
@@ -120,6 +121,10 @@
   }
 
   state->model_version = segment_info.model_version();
+  const proto::SegmentationModelMetadata& model_metadata =
+      segment_info.model_metadata();
+  state->signal_storage_length = model_metadata.signal_storage_length() *
+                                 metadata_utils::GetTimeUnit(model_metadata);
   feature_list_query_processor_->ProcessFeatureList(
       segment_info.model_metadata(), segment_id, clock_->Now(),
       FeatureListQueryProcessor::ProcessOption::kInputsOnly,
@@ -180,7 +185,8 @@
             << optimization_guide::proto::OptimizationTarget_Name(
                    state->segment_id);
     stats::RecordModelExecutionResult(state->segment_id, result.value());
-    if (state->model_version) {
+    if (state->model_version && SegmentationUkmHelper::AllowedToUploadData(
+                                    state->signal_storage_length, clock_)) {
       SegmentationUkmHelper::GetInstance()->RecordModelExecutionResult(
           state->segment_id, state->model_version, state->input_tensor,
           result.value());
diff --git a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
index a94e73d..868d4f4 100644
--- a/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_executor_impl_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/mock_signal_database.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
@@ -27,6 +26,7 @@
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/execution/model_executor.h"
 #include "components/segmentation_platform/internal/execution/processing/mock_feature_list_query_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/aggregation.pb.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
diff --git a/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc b/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
index f591440..4c45592 100644
--- a/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
@@ -6,9 +6,9 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 
 namespace segmentation_platform::processing {
diff --git a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
index dc452a47..b6f5245 100644
--- a/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/feature_list_query_processor.cc
@@ -9,13 +9,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/clock.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/storage_service.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
 #include "components/segmentation_platform/internal/execution/processing/sql_feature_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/uma_feature_processor.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/internal/ukm_data_manager.h"
diff --git a/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc b/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc
index 7b6e835..fc7f7a48 100644
--- a/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/sql_feature_processor.cc
@@ -8,9 +8,9 @@
 #include "base/bind.h"
 #include "base/containers/flat_map.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/execution/processing/custom_input_processor.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 
 namespace segmentation_platform::processing {
diff --git a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
index 0f5b42a5..e3541c1 100644
--- a/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
+++ b/components/segmentation_platform/internal/execution/processing/uma_feature_processor.cc
@@ -9,9 +9,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/timer/elapsed_timer.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/ukm_types.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_processor_state.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/stats.h"
 
diff --git a/components/segmentation_platform/internal/database/metadata_utils.cc b/components/segmentation_platform/internal/metadata/metadata_utils.cc
similarity index 99%
rename from components/segmentation_platform/internal/database/metadata_utils.cc
rename to components/segmentation_platform/internal/metadata/metadata_utils.cc
index a3ceff8..7cf303ed 100644
--- a/components/segmentation_platform/internal/database/metadata_utils.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 
 #include <inttypes.h>
 
diff --git a/components/segmentation_platform/internal/database/metadata_utils.h b/components/segmentation_platform/internal/metadata/metadata_utils.h
similarity index 95%
rename from components/segmentation_platform/internal/database/metadata_utils.h
rename to components/segmentation_platform/internal/metadata/metadata_utils.h
index 50c4b5ae..85ff2bb 100644
--- a/components/segmentation_platform/internal/database/metadata_utils.h
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
-#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
 
 #include "base/time/time.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -119,4 +119,4 @@
 }  // namespace metadata_utils
 }  // namespace segmentation_platform
 
-#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_DATABASE_METADATA_UTILS_H_
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_UTILS_H_
diff --git a/components/segmentation_platform/internal/database/metadata_utils_unittest.cc b/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
similarity index 99%
rename from components/segmentation_platform/internal/database/metadata_utils_unittest.cc
rename to components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
index 5fcbce1..962e8799 100644
--- a/components/segmentation_platform/internal/database/metadata_utils_unittest.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_utils_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 
 #include "base/metrics/metrics_hashes.h"
 #include "components/optimization_guide/proto/models.pb.h"
diff --git a/chrome/browser/segmentation_platform/default_model/metadata_writer.cc b/components/segmentation_platform/internal/metadata/metadata_writer.cc
similarity index 73%
rename from chrome/browser/segmentation_platform/default_model/metadata_writer.cc
rename to components/segmentation_platform/internal/metadata/metadata_writer.cc
index 22740c59..57b52be 100644
--- a/chrome/browser/segmentation_platform/default_model/metadata_writer.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_writer.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/segmentation_platform/default_model/metadata_writer.h"
+#include "components/segmentation_platform/internal/metadata/metadata_writer.h"
 
 #include "base/metrics/metrics_hashes.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
@@ -32,6 +32,25 @@
   }
 }
 
+void MetadataWriter::AddSqlFeatures(const SqlFeature features[],
+                                    size_t features_size) {
+  proto::SqlFeature* feature =
+      metadata_->add_input_features()->mutable_sql_feature();
+  for (size_t i = 0; i < features_size; ++i) {
+    const auto& f = features[i];
+    feature->set_sql(f.sql);
+    for (size_t ev = 0; ev < f.events_size; ++ev) {
+      const auto& event = f.events[ev];
+      auto* ukm_event = feature->mutable_signal_filter()->add_ukm_events();
+      ukm_event->set_event_hash(event.event_hash.GetUnsafeValue());
+      for (size_t m = 0; m < event.metrics_size; ++m) {
+        ukm_event->mutable_metric_hash_filter()->Add(
+            event.metrics[m].GetUnsafeValue());
+      }
+    }
+  }
+}
+
 void MetadataWriter::AddDiscreteMappingEntries(
     const std::string& key,
     const std::pair<float, int>* mappings,
diff --git a/components/segmentation_platform/internal/metadata/metadata_writer.h b/components/segmentation_platform/internal/metadata/metadata_writer.h
new file mode 100644
index 0000000..091a53a
--- /dev/null
+++ b/components/segmentation_platform/internal/metadata/metadata_writer.h
@@ -0,0 +1,97 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
+
+#include <cinttypes>
+#include <cstddef>
+
+#include "components/segmentation_platform/internal/database/ukm_types.h"
+#include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
+
+namespace segmentation_platform {
+
+// Utility to write metadata proto for default models.
+class MetadataWriter {
+ public:
+  explicit MetadataWriter(proto::SegmentationModelMetadata* metadata);
+  ~MetadataWriter();
+
+  MetadataWriter(MetadataWriter&) = delete;
+  MetadataWriter& operator=(MetadataWriter&) = delete;
+
+  // Defines a feature based on UMA metric.
+  struct UMAFeature {
+    const proto::SignalType signal_type{proto::SignalType::UNKNOWN_SIGNAL_TYPE};
+    const char* name{nullptr};
+    const uint64_t bucket_count{0};
+    const uint64_t tensor_length{0};
+    const proto::Aggregation aggregation{proto::Aggregation::UNKNOWN};
+    const size_t enum_ids_size{0};
+    const int32_t* const accepted_enum_ids = nullptr;
+
+    static constexpr UMAFeature FromUserAction(const char* name,
+                                               uint64_t bucket_count) {
+      return MetadataWriter::UMAFeature{
+          .signal_type = proto::SignalType::USER_ACTION,
+          .name = name,
+          .bucket_count = bucket_count,
+          .tensor_length = 1,
+          .aggregation = proto::Aggregation::COUNT,
+          .enum_ids_size = 0};
+    }
+    static constexpr UMAFeature FromEnumHistogram(const char* name,
+                                                  uint64_t bucket_count,
+                                                  const int32_t* const enum_ids,
+                                                  size_t enum_ids_size) {
+      return MetadataWriter::UMAFeature{
+          .signal_type = proto::SignalType::HISTOGRAM_ENUM,
+          .name = name,
+          .bucket_count = bucket_count,
+          .tensor_length = 1,
+          .aggregation = proto::Aggregation::COUNT,
+          .enum_ids_size = enum_ids_size,
+          .accepted_enum_ids = enum_ids};
+    }
+  };
+
+  // Defines a feature based on a SQL query.
+  // TODO(ssid): Support custom inputs.
+  struct SqlFeature {
+    const char* const sql{nullptr};
+    struct EventAndMetrics {
+      const UkmEventHash event_hash;
+      const UkmMetricHash* const metrics{nullptr};
+      const size_t metrics_size{0};
+    };
+    const EventAndMetrics* const events{nullptr};
+    const size_t events_size{0};
+  };
+
+  // Appends the list of UMA features in order.
+  void AddUmaFeatures(const UMAFeature features[], size_t features_size);
+
+  // Appends the list of SQL features in order.
+  void AddSqlFeatures(const SqlFeature features[], size_t features_size);
+
+  // Appends a list of discrete mapping in order.
+  void AddDiscreteMappingEntries(const std::string& key,
+                                 const std::pair<float, int>* mappings,
+                                 size_t mappings_size);
+
+  // Writes the model metadata with the given parameters.
+  void SetSegmentationMetadataConfig(proto::TimeUnit time_unit,
+                                     uint64_t bucket_duration,
+                                     int64_t signal_storage_length,
+                                     int64_t min_signal_collection_length,
+                                     int64_t result_time_to_live);
+
+ private:
+  proto::SegmentationModelMetadata* const metadata_;
+};
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_METADATA_METADATA_WRITER_H_
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.cc b/components/segmentation_platform/internal/scheduler/execution_service.cc
index 7087ea1..590663f 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.cc
+++ b/components/segmentation_platform/internal/scheduler/execution_service.cc
@@ -8,6 +8,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/segmentation_platform/internal/data_collection/training_data_collector.h"
 #include "components/segmentation_platform/internal/database/storage_service.h"
+#include "components/segmentation_platform/internal/execution/default_model_manager.h"
 #include "components/segmentation_platform/internal/execution/model_executor_impl.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_aggregator_impl.h"
 #include "components/segmentation_platform/internal/execution/processing/feature_list_query_processor.h"
@@ -28,10 +29,12 @@
 void ExecutionService::InitForTesting(
     std::unique_ptr<processing::FeatureListQueryProcessor> feature_processor,
     std::unique_ptr<ModelExecutor> executor,
-    std::unique_ptr<ModelExecutionScheduler> scheduler) {
+    std::unique_ptr<ModelExecutionScheduler> scheduler,
+    std::unique_ptr<ModelExecutionManager> execution_manager) {
   feature_list_query_processor_ = std::move(feature_processor);
   model_executor_ = std::move(executor);
   model_execution_scheduler_ = std::move(scheduler);
+  model_execution_manager_ = std::move(execution_manager);
 }
 
 void ExecutionService::Initialize(
@@ -46,6 +49,8 @@
     const PlatformOptions& platform_options,
     std::vector<std::unique_ptr<Config>>* configs,
     PrefService* profile_prefs) {
+  storage_service_ = storage_service;
+
   feature_list_query_processor_ =
       std::make_unique<processing::FeatureListQueryProcessor>(
           storage_service,
@@ -75,27 +80,31 @@
   model_execution_scheduler_->OnNewModelInfoReady(segment_info);
 }
 
+ModelProvider* ExecutionService::GetModelProvider(
+    OptimizationTarget segment_id) {
+  return model_execution_manager_->GetProvider(segment_id);
+}
+
 void ExecutionService::RequestModelExecution(
     std::unique_ptr<ExecutionRequest> request) {
   DCHECK(request->segment_info);
-  if (!request->callback.is_null() && request->model_provider) {
-    DCHECK(!request->save_result_to_db)
-        << "save_result_to_db + callback cannot be set together";
-    model_executor_->ExecuteModel(
-        *request->segment_info, request->model_provider,
-        request->record_metrics_for_default, std::move(request->callback));
-  } else if (request->save_result_to_db) {
+  if (request->save_result_to_db) {
     DCHECK(!request->record_metrics_for_default)
         << "cannot record metics for default model from scheduler";
+    // TODO(ssid): Scheduler should use the `request` instead of fetching the
+    // model provider.
     DCHECK(!request->model_provider)
         << "using custom model provider to save result is not supported";
     DCHECK(request->callback.is_null())
         << "save_result_to_db + callback cannot be set together";
     model_execution_scheduler_->RequestModelExecution(*request->segment_info);
-  } else {
-    NOTREACHED()
-        << "On demand xecution of non-default model is not yet supported";
+    return;
   }
+
+  DCHECK(!request->callback.is_null());
+  model_executor_->ExecuteModel(*request->segment_info, request->model_provider,
+                                request->record_metrics_for_default,
+                                std::move(request->callback));
 }
 
 void ExecutionService::OverwriteModelExecutionResult(
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.h b/components/segmentation_platform/internal/scheduler/execution_service.h
index 49e8028..1b4969f 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.h
+++ b/components/segmentation_platform/internal/scheduler/execution_service.h
@@ -42,7 +42,8 @@
   void InitForTesting(
       std::unique_ptr<processing::FeatureListQueryProcessor> feature_processor,
       std::unique_ptr<ModelExecutor> executor,
-      std::unique_ptr<ModelExecutionScheduler> scheduler);
+      std::unique_ptr<ModelExecutionScheduler> scheduler,
+      std::unique_ptr<ModelExecutionManager> execution_manager);
 
   void Initialize(
       StorageService* storage_service,
@@ -61,8 +62,12 @@
   // SegmentInfo with valid metadata and features.
   void OnNewModelInfoReady(const proto::SegmentInfo& segment_info);
 
+  // Gets the model provider for execution.
+  ModelProvider* GetModelProvider(OptimizationTarget segment_id);
+
   using ModelExecutionCallback =
       base::OnceCallback<void(const std::pair<float, ModelExecutionStatus>&)>;
+
   struct ExecutionRequest {
     ExecutionRequest();
     ~ExecutionRequest();
@@ -95,6 +100,8 @@
   void RunDailyTasks(bool is_startup);
 
  private:
+  raw_ptr<StorageService> storage_service_{};
+
   // Training/inference input data generation.
   std::unique_ptr<processing::FeatureListQueryProcessor>
       feature_list_query_processor_;
@@ -106,7 +113,7 @@
   std::unique_ptr<ModelExecutor> model_executor_;
 
   // Model execution.
-  std::unique_ptr<ModelExecutionManagerImpl> model_execution_manager_;
+  std::unique_ptr<ModelExecutionManager> model_execution_manager_;
 
   // Model execution scheduling logic.
   std::unique_ptr<ModelExecutionScheduler> model_execution_scheduler_;
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index f932d22..baeafe3 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -8,10 +8,10 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/execution/model_execution_manager_impl.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/model_provider.h"
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.cc b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
index e976700a..24bc080 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.cc
@@ -8,10 +8,13 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
+#include "base/time/clock.h"
+#include "components/segmentation_platform/internal/constants.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 
@@ -204,4 +207,14 @@
   return base::bit_cast<int64_t>(static_cast<double>(f));
 }
 
+// static
+bool SegmentationUkmHelper::AllowedToUploadData(
+    base::TimeDelta signal_storage_length,
+    base::Clock* clock) {
+  return LocalStateHelper::GetInstance().GetPrefTime(
+             kSegmentationUkmMostRecentAllowedTimeKey) +
+             signal_storage_length <
+         clock->Now();
+}
+
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper.h b/components/segmentation_platform/internal/segmentation_ukm_helper.h
index 05f33c0..4ceb6e5 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper.h
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/no_destructor.h"
+#include "base/time/time.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -14,6 +15,10 @@
 
 using optimization_guide::proto::OptimizationTarget;
 
+namespace base {
+class Clock;
+}
+
 namespace ukm::builders {
 class Segmentation_ModelExecution;
 }  // namespace ukm::builders
@@ -57,6 +62,11 @@
   // Helper method to encode a float number into int64.
   static int64_t FloatToInt64(float f);
 
+  // Helper method to check if data is allowed to upload through ukm
+  // given a clock and the signal storage length.
+  static bool AllowedToUploadData(base::TimeDelta signal_storage_length,
+                                  base::Clock* clock);
+
   // Gets a set of segment IDs that are allowed to upload metrics.
   const base::flat_set<OptimizationTarget>& allowed_segment_ids() {
     return allowed_segment_ids_;
diff --git a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
index b73ff78..8a3d59ff 100644
--- a/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_ukm_helper_unittest.cc
@@ -9,10 +9,15 @@
 #include "base/bit_cast.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/segmentation_platform/internal/constants.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/features.h"
+#include "components/segmentation_platform/public/local_state_helper.h"
+#include "components/segmentation_platform/public/segmentation_platform_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -256,4 +261,23 @@
   ASSERT_NE(source_id, ukm::kInvalidSourceId);
 }
 
+TEST_F(SegmentationUkmHelperTest, AllowedToUploadData) {
+  TestingPrefServiceSimple prefs;
+  SegmentationPlatformService::RegisterLocalStatePrefs(prefs.registry());
+  LocalStateHelper::GetInstance().Initialize(&prefs);
+
+  base::SimpleTestClock clock;
+  clock.SetNow(base::Time::Now());
+  LocalStateHelper::GetInstance().SetPrefTime(
+      kSegmentationUkmMostRecentAllowedTimeKey, clock.Now());
+
+  ASSERT_FALSE(
+      SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+  clock.Advance(base::Seconds(10));
+  ASSERT_TRUE(
+      SegmentationUkmHelper::AllowedToUploadData(base::Seconds(1), &clock));
+  ASSERT_FALSE(
+      SegmentationUkmHelper::AllowedToUploadData(base::Seconds(11), &clock));
+}
+
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/experimental_group_recorder.cc b/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
index d02fd90..43137e5 100644
--- a/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
+++ b/components/segmentation_platform/internal/selection/experimental_group_recorder.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "base/strings/strcat.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/metric_filter_utils.h"
 #include "components/segmentation_platform/public/config.h"
 #include "components/segmentation_platform/public/field_trial_register.h"
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index 8c9ec0c..79029e1 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -9,10 +9,10 @@
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/scheduler/execution_service.h"
@@ -34,6 +34,22 @@
   return rank;
 }
 
+proto::SegmentInfo* GetSegmentInfo(
+    DefaultModelManager::SegmentInfoList& available_segments,
+    bool default_model) {
+  proto::SegmentInfo* segment_info = nullptr;
+  DefaultModelManager::SegmentSource needed_source =
+      default_model ? DefaultModelManager::SegmentSource::DEFAULT_MODEL
+                    : DefaultModelManager::SegmentSource::DATABASE;
+  for (const auto& info : available_segments) {
+    if (info->segment_source == needed_source) {
+      segment_info = &info->segment_info;
+      break;
+    }
+  }
+  return segment_info;
+}
+
 class SegmentResultProviderImpl : public SegmentResultProvider {
  public:
   SegmentResultProviderImpl(SegmentInfoDatabase* segment_database,
@@ -50,33 +66,33 @@
         force_refresh_results_(force_refresh_results),
         task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
 
-  void GetSegmentResult(OptimizationTarget segment_id,
-                        const std::string& segmentation_key,
-                        SegmentResultCallback callback) override;
+  void GetSegmentResult(GetResultOptions&& options) override;
 
   SegmentResultProviderImpl(SegmentResultProviderImpl&) = delete;
   SegmentResultProviderImpl& operator=(SegmentResultProviderImpl&) = delete;
 
  private:
   struct RequestState {
-    OptimizationTarget segment_id;
-    SegmentResultCallback callback;
     raw_ptr<ModelProvider> default_provider;
-    std::string segmentation_key;
+    GetResultOptions options;
   };
 
   void OnGetSegmentInfo(
       std::unique_ptr<RequestState> request_state,
       DefaultModelManager::SegmentInfoList available_segments);
 
+  void TryExecuteModelAndGetScore(
+      std::unique_ptr<RequestState> request_state,
+      DefaultModelManager::SegmentInfoList available_segments);
+
   void TryGetScoreFromDefaultModel(
       std::unique_ptr<RequestState> request_state,
       SegmentResultProvider::ResultState existing_state,
       DefaultModelManager::SegmentInfoList available_segments);
-  void OnDefaultModelExecuted(
-      std::unique_ptr<RequestState> request_state,
-      std::unique_ptr<proto::SegmentInfo> segment_info,
-      const std::pair<float, ModelExecutionStatus>& result);
+  void OnModelExecuted(std::unique_ptr<RequestState> request_state,
+                       std::unique_ptr<proto::SegmentInfo> segment_info,
+                       bool is_metrics_for_default,
+                       const std::pair<float, ModelExecutionStatus>& result);
 
   void PostResultCallback(std::unique_ptr<RequestState> request_state,
                           std::unique_ptr<SegmentResult> result);
@@ -92,15 +108,11 @@
   base::WeakPtrFactory<SegmentResultProviderImpl> weak_ptr_factory_{this};
 };
 
-void SegmentResultProviderImpl::GetSegmentResult(
-    OptimizationTarget segment_id,
-    const std::string& segmentation_key,
-    SegmentResultCallback callback) {
+void SegmentResultProviderImpl::GetSegmentResult(GetResultOptions&& options) {
+  const OptimizationTarget segment_id = options.segment_id;
   auto request_state = std::make_unique<RequestState>();
   request_state = std::make_unique<RequestState>();
-  request_state->segment_id = segment_id;
-  request_state->segmentation_key = segmentation_key;
-  request_state->callback = std::move(callback);
+  request_state->options = std::move(options);
   // Factory can be null in tests.
   request_state->default_provider =
       default_model_manager_
@@ -116,20 +128,14 @@
 void SegmentResultProviderImpl::OnGetSegmentInfo(
     std::unique_ptr<RequestState> request_state,
     DefaultModelManager::SegmentInfoList available_segments) {
-  const proto::SegmentInfo* db_segment_info = nullptr;
-  for (const auto& info : available_segments) {
-    DCHECK_EQ(request_state->segment_id, info->segment_info.segment_id());
-    if (info->segment_source == DefaultModelManager::SegmentSource::DATABASE) {
-      db_segment_info = &info->segment_info;
-      break;
-    }
-  }
+  const proto::SegmentInfo* db_segment_info =
+      GetSegmentInfo(available_segments, /*default_model=*/false);
 
   // Don't compute results if we don't have enough signals, or don't have
   // valid unexpired results for any of the segments.
   if (!db_segment_info) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->segment_id)
+            << OptimizationTarget_Name(request_state->options.segment_id)
             << " does not have segment info.";
     TryGetScoreFromDefaultModel(std::move(request_state),
                                 ResultState::kSegmentNotAvailable,
@@ -137,8 +143,8 @@
     return;
   }
 
-  // TODO(ssid): Remove this check since scheduler does this before executing
-  // the model.
+  // TODO(ssid): Remove this check when using database results since the
+  // scheduler does this before executing the model.
   if (!force_refresh_results_ &&
       !signal_storage_config_->MeetsSignalCollectionRequirement(
           db_segment_info->model_metadata())) {
@@ -151,6 +157,15 @@
     return;
   }
 
+  if (request_state->options.ignore_db_scores) {
+    VLOG(1) << __func__ << ": segment="
+            << OptimizationTarget_Name(db_segment_info->segment_id())
+            << " executing model to get score";
+    TryExecuteModelAndGetScore(std::move(request_state),
+                               std::move(available_segments));
+    return;
+  }
+
   if (metadata_utils::HasExpiredOrUnavailableResult(*db_segment_info,
                                                     clock_->Now())) {
     VLOG(1) << __func__ << ": segment="
@@ -162,13 +177,36 @@
     return;
   }
 
-  int rank =
-      ComputeDiscreteMapping(request_state->segmentation_key, *db_segment_info);
+  int rank = ComputeDiscreteMapping(request_state->options.segmentation_key,
+                                    *db_segment_info);
   PostResultCallback(
       std::move(request_state),
       std::make_unique<SegmentResult>(ResultState::kSuccessFromDatabase, rank));
 }
 
+void SegmentResultProviderImpl::TryExecuteModelAndGetScore(
+    std::unique_ptr<RequestState> request_state,
+    DefaultModelManager::SegmentInfoList available_segments) {
+  auto db_segment_info = std::make_unique<proto::SegmentInfo>();
+  proto::SegmentInfo* segment_info =
+      GetSegmentInfo(available_segments, /*default_model=*/false);
+  // Note: The db segment info in `available_segments` is no longer usable.
+  db_segment_info->Swap(segment_info);
+  DCHECK(db_segment_info);
+
+  auto request = std::make_unique<ExecutionService::ExecutionRequest>();
+  // The pointer is kept alive by the unique_ptr in the callback.
+  request->segment_info = db_segment_info.get();
+  request->model_provider =
+      execution_service_->GetModelProvider(db_segment_info->segment_id());
+  request->record_metrics_for_default = true;
+  request->callback = base::BindOnce(
+      &SegmentResultProviderImpl::OnModelExecuted,
+      weak_ptr_factory_.GetWeakPtr(), std::move(request_state),
+      std::move(db_segment_info), /*is_metrics_for_default=*/false);
+  execution_service_->RequestModelExecution(std::move(request));
+}
+
 void SegmentResultProviderImpl::TryGetScoreFromDefaultModel(
     std::unique_ptr<RequestState> request_state,
     SegmentResultProvider::ResultState existing_state,
@@ -176,27 +214,18 @@
   if (!request_state->default_provider ||
       !request_state->default_provider->ModelAvailable()) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->segment_id)
+            << OptimizationTarget_Name(request_state->options.segment_id)
             << " default provider not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(existing_state));
     return;
   }
 
-  std::unique_ptr<proto::SegmentInfo> default_segment_info;
-  for (auto& info : available_segments) {
-    DCHECK_EQ(request_state->segment_id, info->segment_info.segment_id());
-    if (info->segment_source ==
-        DefaultModelManager::SegmentSource::DEFAULT_MODEL) {
-      default_segment_info = std::make_unique<proto::SegmentInfo>();
-      default_segment_info->Swap(&info->segment_info);
-      break;
-    }
-  }
-
-  if (!default_segment_info) {
+  proto::SegmentInfo* segment_info =
+      GetSegmentInfo(available_segments, /*default_model=*/true);
+  if (!segment_info) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->segment_id)
+            << OptimizationTarget_Name(request_state->options.segment_id)
             << " default segment info not available";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
@@ -204,6 +233,9 @@
     return;
   }
 
+  auto default_segment_info = std::make_unique<proto::SegmentInfo>();
+  default_segment_info->Swap(segment_info);
+
   DCHECK_EQ(
       metadata_utils::ValidationResult::kValidationSuccess,
       metadata_utils::ValidateMetadata(default_segment_info->model_metadata()));
@@ -211,7 +243,7 @@
       !signal_storage_config_->MeetsSignalCollectionRequirement(
           default_segment_info->model_metadata())) {
     VLOG(1) << __func__ << ": segment="
-            << OptimizationTarget_Name(request_state->segment_id)
+            << OptimizationTarget_Name(request_state->options.segment_id)
             << " signal collection not met";
     PostResultCallback(std::move(request_state),
                        std::make_unique<SegmentResult>(
@@ -225,29 +257,36 @@
   // The pointer is kept alive by the unique_ptr in the callback.
   request->segment_info = default_segment_info.get();
   request->record_metrics_for_default = true;
-  request->callback =
-      base::BindOnce(&SegmentResultProviderImpl::OnDefaultModelExecuted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(request_state),
-                     std::move(default_segment_info));
+  request->callback = base::BindOnce(
+      &SegmentResultProviderImpl::OnModelExecuted,
+      weak_ptr_factory_.GetWeakPtr(), std::move(request_state),
+      std::move(default_segment_info), /*is_metrics_for_default=*/true);
   request->model_provider = default_provider;
   execution_service_->RequestModelExecution(std::move(request));
 }
 
-void SegmentResultProviderImpl::OnDefaultModelExecuted(
+void SegmentResultProviderImpl::OnModelExecuted(
     std::unique_ptr<RequestState> request_state,
     std::unique_ptr<proto::SegmentInfo> segment_info,
+    bool is_metrics_for_default,
     const std::pair<float, ModelExecutionStatus>& result) {
   if (result.second == ModelExecutionStatus::kSuccess) {
     segment_info->mutable_prediction_result()->set_result(result.first);
-    int rank =
-        ComputeDiscreteMapping(request_state->segmentation_key, *segment_info);
+    int rank = ComputeDiscreteMapping(request_state->options.segmentation_key,
+                                      *segment_info);
+    ResultState state = is_metrics_for_default
+                            ? ResultState::kDefaultModelScoreUsed
+                            : ResultState::kTfliteModelScoreUsed;
     PostResultCallback(std::move(request_state),
-                       std::make_unique<SegmentResult>(
-                           ResultState::kDefaultModelScoreUsed, rank));
+                       std::make_unique<SegmentResult>(state, rank));
   } else {
+    // TODO(ssid): If the real model execution failed, fallback to default
+    // execution.
+    ResultState state = is_metrics_for_default
+                            ? ResultState::kDefaultModelExecutionFailed
+                            : ResultState::kTfliteModelExecutionFailed;
     PostResultCallback(std::move(request_state),
-                       std::make_unique<SegmentResult>(
-                           ResultState::kDefaultModelExecutionFailed));
+                       std::make_unique<SegmentResult>(state));
   }
 }
 
@@ -255,8 +294,8 @@
     std::unique_ptr<RequestState> request_state,
     std::unique_ptr<SegmentResult> result) {
   task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(std::move(request_state->callback), std::move(result)));
+      FROM_HERE, base::BindOnce(std::move(request_state->options.callback),
+                                std::move(result)));
 }
 
 }  // namespace
@@ -267,6 +306,12 @@
     : state(state), rank(rank) {}
 SegmentResultProvider::SegmentResult::~SegmentResult() = default;
 
+SegmentResultProvider::GetResultOptions::GetResultOptions() = default;
+SegmentResultProvider::GetResultOptions::~GetResultOptions() = default;
+SegmentResultProvider::GetResultOptions&
+SegmentResultProvider::GetResultOptions::operator=(GetResultOptions&&) =
+    default;
+
 // static
 std::unique_ptr<SegmentResultProvider> SegmentResultProvider::Create(
     SegmentInfoDatabase* segment_database,
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.h b/components/segmentation_platform/internal/selection/segment_result_provider.h
index fa3bd4b..fb540f8 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.h
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.h
@@ -20,6 +20,12 @@
 class SignalStorageConfig;
 
 // Used for retrieving the result of a particular model.
+// Supports 3 use cases:
+//  1. Fetching cached and valid results from the segment database.
+//  2. Fallback to default model when cached results are missing. Executes the
+//     default model and provides the result.
+//  3. Execute the TFLite model and provide the result when `ignore_db_scores`
+//     is set.
 class SegmentResultProvider {
  public:
   SegmentResultProvider() = default;
@@ -35,6 +41,8 @@
     kDefaultModelMetadataMissing = 6,
     kDefaultModelExecutionFailed = 7,
     kDefaultModelScoreUsed = 8,
+    kTfliteModelExecutionFailed = 9,
+    kTfliteModelScoreUsed = 10,
   };
   struct SegmentResult {
     explicit SegmentResult(ResultState state);
@@ -58,10 +66,34 @@
       base::Clock* clock,
       bool force_refresh_results);
 
+  // Options for `GetSegmentResult()`.
+  struct GetResultOptions {
+    GetResultOptions();
+    ~GetResultOptions();
+    GetResultOptions& operator=(GetResultOptions&&);
+
+    // The segment ID to fetch result for.
+    OptimizationTarget segment_id =
+        OptimizationTarget::OPTIMIZATION_TARGET_UNKNOWN;
+
+    // The key is used for recording metrics only.
+    std::string segmentation_key;
+
+    // Ignores model results stored in database and executes them to fetch
+    // results. When set to false, the result could be from following:
+    //  * Score cached in the database
+    //  * Execution of default model when score is missing.
+    // When set to true, the result could be from following:
+    //  * Execution of TFLite model.
+    //  * TODO(ssid): Support fallback to default when model is missing.
+    bool ignore_db_scores = false;
+
+    // Callback to return the segment result.
+    SegmentResultCallback callback;
+  };
+
   // Returns latest available score for the segment.
-  virtual void GetSegmentResult(OptimizationTarget segment_id,
-                                const std::string& segmentation_key,
-                                SegmentResultCallback callback) = 0;
+  virtual void GetSegmentResult(GetResultOptions&& options) = 0;
 };
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
index f0858e9..1355425 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -34,15 +34,15 @@
 const OptimizationTarget kTestSegment2 =
     OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE;
 
-constexpr float kDefaultScore = 0.1;
-constexpr float kModelScore = 0.6;
-constexpr int kDefaultRank = 0;
-constexpr int kModelRank = 1;
+constexpr float kTestScore = 0.1;
+constexpr float kDatabaseScore = 0.6;
+constexpr int kTestRank = 0;
+constexpr int kDatabaseRank = 1;
 
-class DefaultProvider : public ModelProvider {
+class TestModelProvider : public ModelProvider {
  public:
   static constexpr int64_t kVersion = 10;
-  explicit DefaultProvider(OptimizationTarget segment)
+  explicit TestModelProvider(OptimizationTarget segment)
       : ModelProvider(segment) {}
 
   void InitAndFetchModel(
@@ -54,13 +54,20 @@
 
   void ExecuteModelWithInput(const std::vector<float>& inputs,
                              ExecutionCallback callback) override {
-    std::move(callback).Run(kDefaultScore);
+    std::move(callback).Run(kTestScore);
   }
 
   // Returns true if a model is available.
   bool ModelAvailable() override { return true; }
 };
 
+class MockModelExecutionManager : public ModelExecutionManager {
+ public:
+  MOCK_METHOD(ModelProvider*,
+              GetProvider,
+              (optimization_guide::proto::OptimizationTarget segment_id));
+};
+
 }  // namespace
 
 class SegmentResultProviderTest : public testing::Test {
@@ -77,10 +84,13 @@
     auto query_processor =
         std::make_unique<processing::MockFeatureListQueryProcessor>();
     mock_query_processor_ = query_processor.get();
+    auto moved_execution_manager =
+        std::make_unique<MockModelExecutionManager>();
+    mock_execution_manager_ = moved_execution_manager.get();
     execution_service_->InitForTesting(
         std::move(query_processor),
         std::make_unique<ModelExecutorImpl>(&clock_, mock_query_processor_),
-        nullptr);
+        nullptr, std::move(moved_execution_manager));
     score_provider_ = SegmentResultProvider::Create(
         segment_database_.get(), &signal_storage_config_,
         default_manager_.get(), execution_service_.get(), &clock_,
@@ -95,24 +105,28 @@
 
   void ExpectSegmentResultOnGet(
       OptimizationTarget segment_id,
+      bool ignore_db_scores,
       SegmentResultProvider::ResultState expected_state,
       absl::optional<int> expected_rank) {
     base::RunLoop wait_for_result;
-    score_provider_->GetSegmentResult(
-        segment_id, "test_key",
-        base::BindOnce(
-            [](SegmentResultProvider::ResultState expected_state,
-               absl::optional<int> expected_rank, base::OnceClosure quit,
-               std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
-              EXPECT_EQ(result->state, expected_state);
-              if (expected_rank) {
-                EXPECT_EQ(*expected_rank, result->rank);
-              } else {
-                EXPECT_FALSE(result->rank);
-              }
-              std::move(quit).Run();
-            },
-            expected_state, expected_rank, wait_for_result.QuitClosure()));
+    SegmentResultProvider::GetResultOptions options;
+    options.segment_id = segment_id;
+    options.segmentation_key = "test_key";
+    options.ignore_db_scores = ignore_db_scores;
+    options.callback = base::BindOnce(
+        [](SegmentResultProvider::ResultState expected_state,
+           absl::optional<int> expected_rank, base::OnceClosure quit,
+           std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
+          EXPECT_EQ(result->state, expected_state);
+          if (expected_rank) {
+            EXPECT_EQ(*expected_rank, result->rank);
+          } else {
+            EXPECT_FALSE(result->rank);
+          }
+          std::move(quit).Run();
+        },
+        expected_state, expected_rank, wait_for_result.QuitClosure());
+    score_provider_->GetSegmentResult(std::move(options));
     wait_for_result.Run();
   }
 
@@ -140,8 +154,8 @@
 
     // Initialize metadata so that score from default model returns default rank
     // and score from model score returns model rank.
-    float mapping[][2] = {{kDefaultScore + 0.1, kDefaultRank},
-                          {kModelScore - 0.1, kModelRank}};
+    float mapping[][2] = {{kTestScore + 0.1, kTestRank},
+                          {kDatabaseScore - 0.1, kDatabaseRank}};
     segment_database_->AddDiscreteMapping(segment_id, mapping, 2, "test_key");
   }
 
@@ -152,6 +166,7 @@
   TestModelProviderFactory provider_factory_;
   MockSignalDatabase signal_database_;
   processing::MockFeatureListQueryProcessor* mock_query_processor_ = nullptr;
+  MockModelExecutionManager* mock_execution_manager_;
   SignalHandler signal_handler_;
   std::unique_ptr<DefaultModelManager> default_manager_;
   std::unique_ptr<ExecutionService> execution_service_;
@@ -163,8 +178,8 @@
 
 TEST_F(SegmentResultProviderTest, GetScoreWithoutInfo) {
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kSegmentNotAvailable,
-      absl::nullopt);
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kSegmentNotAvailable, absl::nullopt);
 }
 
 TEST_F(SegmentResultProviderTest, GetScoreFromDbWithoutResult) {
@@ -173,7 +188,8 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(true));
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kDatabaseScoreNotReady,
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kDatabaseScoreNotReady,
       absl::nullopt);
 }
 
@@ -183,25 +199,83 @@
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(false));
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kSignalsNotCollected,
-      absl::nullopt);
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kSignalsNotCollected, absl::nullopt);
 }
 
 TEST_F(SegmentResultProviderTest, GetScoreFromDb) {
   InitializeMetadata(kTestSegment);
-  SetSegmentResult(kTestSegment, kModelScore);
+  SetSegmentResult(kTestSegment, kDatabaseScore);
 
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
       .WillOnce(Return(true));
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kSuccessFromDatabase,
-      kModelRank);
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kSuccessFromDatabase, kDatabaseRank);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromModelNotEnoughSignals) {
+  SetSegmentResult(kTestSegment, absl::nullopt);
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+      .WillOnce(Return(false));
+  ExpectSegmentResultOnGet(
+      kTestSegment, /*ignore_db_scores=*/true,
+      SegmentResultProvider::ResultState::kSignalsNotCollected, absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromModelExecutionFailed) {
+  InitializeMetadata(kTestSegment);
+  SetSegmentResult(kTestSegment, kDatabaseScore);
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+      .Times(2)
+      .WillRepeatedly(Return(true));
+
+  // No model available to execute.
+  EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+      .WillOnce(Return(nullptr));
+  ExpectSegmentResultOnGet(
+      kTestSegment, /*ignore_db_scores=*/true,
+      SegmentResultProvider::ResultState::kTfliteModelExecutionFailed,
+      absl::nullopt);
+
+  // Feature processing failed.
+  TestModelProvider provider(kTestSegment);
+  EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+      .WillOnce(Return(&provider));
+  EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _))
+      .WillOnce(RunOnceCallback<4>(/*error=*/true, std::vector<float>{{1, 2}},
+                                   std::vector<float>()));
+  ExpectSegmentResultOnGet(
+      kTestSegment, /*ignore_db_scores=*/true,
+      SegmentResultProvider::ResultState::kTfliteModelExecutionFailed,
+      absl::nullopt);
+}
+
+TEST_F(SegmentResultProviderTest, GetFromModel) {
+  InitializeMetadata(kTestSegment);
+  SetSegmentResult(kTestSegment, kDatabaseScore);
+
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+      .WillOnce(Return(true));
+
+  TestModelProvider provider(kTestSegment);
+  EXPECT_CALL(*mock_execution_manager_, GetProvider(kTestSegment))
+      .WillOnce(Return(&provider));
+  EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _))
+      .WillOnce(RunOnceCallback<4>(/*error=*/false, std::vector<float>{{1, 2}},
+                                   std::vector<float>()));
+
+  // Gets the rank from test model instead of database.
+  ExpectSegmentResultOnGet(
+      kTestSegment, /*ignore_db_scores=*/true,
+      SegmentResultProvider::ResultState::kTfliteModelScoreUsed, kTestRank);
 }
 
 TEST_F(SegmentResultProviderTest, DefaultNeedsSignal) {
   SetSegmentResult(kTestSegment, absl::nullopt);
   std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
-  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
   // First call is to check opt guide model, and second is to check default
@@ -211,6 +285,7 @@
       .WillOnce(Return(false));
   ExpectSegmentResultOnGet(
       kTestSegment,
+      /*ignore_db_scores=*/false,
       SegmentResultProvider::ResultState::kDefaultModelSignalNotCollected,
       absl::nullopt);
 }
@@ -218,7 +293,7 @@
 TEST_F(SegmentResultProviderTest, DefaultModelFailedExecution) {
   SetSegmentResult(kTestSegment, absl::nullopt);
   std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
-  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
@@ -231,6 +306,7 @@
                                    std::vector<float>()));
   ExpectSegmentResultOnGet(
       kTestSegment,
+      /*ignore_db_scores=*/false,
       SegmentResultProvider::ResultState::kDefaultModelExecutionFailed,
       absl::nullopt);
 }
@@ -238,7 +314,7 @@
 TEST_F(SegmentResultProviderTest, GetFromDefault) {
   SetSegmentResult(kTestSegment, absl::nullopt);
   std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
-  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
+  p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
@@ -248,19 +324,19 @@
       .WillOnce(RunOnceCallback<4>(/*error=*/false, std::vector<float>{{1, 2}},
                                    std::vector<float>()));
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
-      kDefaultRank);
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
 }
 
 TEST_F(SegmentResultProviderTest, MultipleRequests) {
   InitializeMetadata(kTestSegment);
   SetSegmentResult(kTestSegment, absl::nullopt);
   InitializeMetadata(kTestSegment2);
-  SetSegmentResult(kTestSegment2, kModelScore);
+  SetSegmentResult(kTestSegment2, kDatabaseScore);
 
   std::map<OptimizationTarget, std::unique_ptr<ModelProvider>> p;
-  p.emplace(kTestSegment, std::make_unique<DefaultProvider>(kTestSegment));
-  p.emplace(kTestSegment2, std::make_unique<DefaultProvider>(kTestSegment2));
+  p.emplace(kTestSegment, std::make_unique<TestModelProvider>(kTestSegment));
+  p.emplace(kTestSegment2, std::make_unique<TestModelProvider>(kTestSegment2));
   default_manager_->SetDefaultProvidersForTesting(std::move(p));
 
   // For the first request, the database does not have valid result, and default
@@ -272,8 +348,8 @@
       .WillOnce(RunOnceCallback<4>(/*error=*/false, std::vector<float>{{1, 2}},
                                    std::vector<float>()));
   ExpectSegmentResultOnGet(
-      kTestSegment, SegmentResultProvider::ResultState::kDefaultModelScoreUsed,
-      kDefaultRank);
+      kTestSegment, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kDefaultModelScoreUsed, kTestRank);
 
   // For the second request the database has valid result.
   EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
@@ -281,8 +357,8 @@
   EXPECT_CALL(*mock_query_processor_, ProcessFeatureList(_, _, _, _, _))
       .Times(0);
   ExpectSegmentResultOnGet(
-      kTestSegment2, SegmentResultProvider::ResultState::kSuccessFromDatabase,
-      kModelRank);
+      kTestSegment2, /*ignore_db_scores=*/false,
+      SegmentResultProvider::ResultState::kSuccessFromDatabase, kDatabaseRank);
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/selection/segment_score_provider.cc b/components/segmentation_platform/internal/selection/segment_score_provider.cc
index eda9084b..6c05a9bc 100644
--- a/components/segmentation_platform/internal/selection/segment_score_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_score_provider.cc
@@ -8,8 +8,8 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector.h b/components/segmentation_platform/internal/selection/segment_selector.h
index eebbc0491..9b426e6b 100644
--- a/components/segmentation_platform/internal/selection/segment_selector.h
+++ b/components/segmentation_platform/internal/selection/segment_selector.h
@@ -36,6 +36,11 @@
   // asynchronously. If none, returns empty result.
   virtual void GetSelectedSegment(SegmentSelectionCallback callback) = 0;
 
+  // Client API. Runs models and selects a segment on demand. Returns empty
+  // result on failure.
+  virtual void GetSelectedSegmentOnDemand(
+      SegmentSelectionCallback callback) = 0;
+
   // Client API. Returns the cached selected segment from the last session
   // synchronously.
   virtual SegmentSelectionResult GetCachedSegmentResult() = 0;
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index 28a01530..a3b584d5 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -13,9 +13,9 @@
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/metric_filter_utils.h"
 #include "components/segmentation_platform/internal/platform_options.h"
 #include "components/segmentation_platform/internal/proto/model_metadata.pb.h"
@@ -37,6 +37,7 @@
     case SegmentResultProvider::ResultState::kUnknown:
     case SegmentResultProvider::ResultState::kSuccessFromDatabase:
     case SegmentResultProvider::ResultState::kDefaultModelScoreUsed:
+    case SegmentResultProvider::ResultState::kTfliteModelScoreUsed:
       NOTREACHED();
       return stats::SegmentationSelectionFailureReason::kMaxValue;
     case SegmentResultProvider::ResultState::kDatabaseScoreNotReady:
@@ -57,6 +58,9 @@
     case SegmentResultProvider::ResultState::kDefaultModelExecutionFailed:
       return stats::SegmentationSelectionFailureReason::
           kAtLeastOneSegmentDefaultExecFailed;
+    case SegmentResultProvider::ResultState::kTfliteModelExecutionFailed:
+      return stats::SegmentationSelectionFailureReason::
+          kAtLeastOneSegmentTfliteExecFailed;
   }
 }
 
@@ -136,7 +140,7 @@
       segment_database_, signal_storage_config_, default_model_manager_,
       execution_service, clock_, platform_options_.force_refresh_results);
   if (IsPreviousSelectionInvalid()) {
-    GetRankForNextSegment(std::make_unique<SegmentRanks>());
+    SelectSegmentAndStoreToPrefs();
   }
 
   // If the segment selection is ready, also record the subsegment for all the
@@ -164,6 +168,12 @@
   return selected_segment_last_session_;
 }
 
+void SegmentSelectorImpl::GetSelectedSegmentOnDemand(
+    SegmentSelectionCallback callback) {
+  DCHECK(config_->on_demand_execution);
+  GetRankForNextSegment(std::make_unique<SegmentRanks>(), std::move(callback));
+}
+
 void SegmentSelectorImpl::OnModelExecutionCompleted(
     OptimizationTarget segment_id) {
   DCHECK(segment_result_provider_);
@@ -175,7 +185,7 @@
   if (!IsPreviousSelectionInvalid())
     return;
 
-  GetRankForNextSegment(std::make_unique<SegmentRanks>());
+  SelectSegmentAndStoreToPrefs();
 }
 
 bool SegmentSelectorImpl::IsPreviousSelectionInvalid() {
@@ -203,24 +213,50 @@
   return true;
 }
 
+void SegmentSelectorImpl::SelectSegmentAndStoreToPrefs() {
+  if (config_->on_demand_execution) {
+    return;
+  }
+  GetRankForNextSegment(std::make_unique<SegmentRanks>(),
+                        SegmentSelectionCallback());
+}
+
 void SegmentSelectorImpl::GetRankForNextSegment(
-    std::unique_ptr<SegmentRanks> ranks) {
+    std::unique_ptr<SegmentRanks> ranks,
+    SegmentSelectionCallback callback) {
   for (OptimizationTarget needed_segment : config_->segment_ids) {
     if (ranks->count(needed_segment) == 0) {
-      segment_result_provider_->GetSegmentResult(
-          needed_segment, config_->segmentation_key,
+      SegmentResultProvider::GetResultOptions options;
+      options.segment_id = needed_segment;
+      options.segmentation_key = config_->segmentation_key;
+      options.ignore_db_scores = config_->on_demand_execution;
+      options.callback =
           base::BindOnce(&SegmentSelectorImpl::OnGetResultForSegmentSelection,
                          weak_ptr_factory_.GetWeakPtr(), std::move(ranks),
-                         needed_segment));
+                         std::move(callback), needed_segment);
+
+      segment_result_provider_->GetSegmentResult(std::move(options));
       return;
     }
   }
+
+  // Finished fetching ranks for all segments.
   OptimizationTarget selected_segment = FindBestSegment(*ranks);
-  UpdateSelectedSegment(selected_segment);
+  if (config_->on_demand_execution) {
+    DCHECK(!callback.is_null());
+    SegmentSelectionResult result;
+    result.is_ready = true;
+    result.segment = selected_segment;
+    std::move(callback).Run(result);
+  } else {
+    DCHECK(callback.is_null());
+    UpdateSelectedSegment(selected_segment);
+  }
 }
 
 void SegmentSelectorImpl::OnGetResultForSegmentSelection(
     std::unique_ptr<SegmentRanks> ranks,
+    SegmentSelectionCallback callback,
     OptimizationTarget current_segment_id,
     std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
   if (!result->rank) {
@@ -230,7 +266,7 @@
   }
   ranks->insert(std::make_pair(current_segment_id, *result->rank));
 
-  GetRankForNextSegment(std::move(ranks));
+  GetRankForNextSegment(std::move(ranks), std::move(callback));
 }
 
 OptimizationTarget SegmentSelectorImpl::FindBestSegment(
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.h b/components/segmentation_platform/internal/selection/segment_selector_impl.h
index ce1ab1e..003d26d 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.h
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.h
@@ -56,6 +56,7 @@
   void OnPlatformInitialized(ExecutionService* execution_service) override;
   void GetSelectedSegment(SegmentSelectionCallback callback) override;
   SegmentSelectionResult GetCachedSegmentResult() override;
+  void GetSelectedSegmentOnDemand(SegmentSelectionCallback callback) override;
 
   // Helper function to update the selected segment in the prefs. Auto-extends
   // the selection if the new result is unknown.
@@ -65,6 +66,11 @@
   // best segment, and writes it to the pref.
   void OnModelExecutionCompleted(OptimizationTarget segment_id) override;
 
+  void set_segment_result_provider_for_testing(
+      std::unique_ptr<SegmentResultProvider> result_provider) {
+    segment_result_provider_ = std::move(result_provider);
+  }
+
  private:
   // For testing.
   friend class SegmentSelectorTest;
@@ -75,13 +81,19 @@
   // segment selection TTL has expired, or selection is unavailable.
   bool IsPreviousSelectionInvalid();
 
+  // Gets scores for all segments and recomputes selection and stores the result
+  // to prefs.
+  void SelectSegmentAndStoreToPrefs();
+
   // Gets ranks for each segment from SegmentResultProvider, and then computes
   // segment selection.
-  void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks);
+  void GetRankForNextSegment(std::unique_ptr<SegmentRanks> ranks,
+                             SegmentSelectionCallback callback);
 
   // Callback used to get result from SegmentResultProvider for each segment.
   void OnGetResultForSegmentSelection(
       std::unique_ptr<SegmentRanks> ranks,
+      SegmentSelectionCallback callback,
       OptimizationTarget current_segment_id,
       std::unique_ptr<SegmentResultProvider::SegmentResult> result);
 
diff --git a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
index 1a8e4df..be23e38 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_unittest.cc
@@ -8,12 +8,12 @@
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
 #include "components/segmentation_platform/internal/constants.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/mock_signal_storage_config.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/test_segment_info_database.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
 #include "components/segmentation_platform/internal/execution/mock_model_provider.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/metric_filter_utils.h"
 #include "components/segmentation_platform/internal/selection/segmentation_result_prefs.h"
 #include "components/segmentation_platform/public/config.h"
@@ -56,6 +56,11 @@
   return config;
 }
 
+class MockResultProvider : public SegmentResultProvider {
+ public:
+  MOCK_METHOD1(GetSegmentResult, void(GetResultOptions&& options));
+};
+
 }  // namespace
 
 class TestSegmentationResultPrefs : public SegmentationResultPrefs {
@@ -169,6 +174,51 @@
   ASSERT_EQ(segment_id2, prefs_->selection->segment_id);
 }
 
+TEST_F(SegmentSelectorTest, RunSelectionOnDemand) {
+  Config config = CreateTestConfig();
+  config.on_demand_execution = true;
+  SetUpWithConfig(config);
+  EXPECT_CALL(signal_storage_config_, MeetsSignalCollectionRequirement(_, _))
+      .WillRepeatedly(Return(true));
+
+  static constexpr OptimizationTarget kSegmentId =
+      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  float mapping[][2] = {{0.2, 1}, {0.5, 3}, {0.7, 4}};
+  InitializeMetadataForSegment(kSegmentId, mapping, 3);
+
+  static constexpr OptimizationTarget kSegmentId2 =
+      OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
+  float mapping2[][2] = {{0.3, 1}, {0.4, 4}};
+  InitializeMetadataForSegment(kSegmentId2, mapping2, 2);
+
+  auto result_provider = std::make_unique<MockResultProvider>();
+  EXPECT_CALL(*result_provider, GetSegmentResult(_))
+      .Times(2)
+      .WillRepeatedly(
+          Invoke([](SegmentResultProvider::GetResultOptions&& options) {
+            EXPECT_TRUE(options.ignore_db_scores);
+            int rank = options.segment_id == kSegmentId ? 3 : 4;
+            auto result =
+                std::make_unique<SegmentResultProvider::SegmentResult>(
+                    SegmentResultProvider::ResultState::kTfliteModelScoreUsed,
+                    rank);
+            std::move(options.callback).Run(std::move(result));
+          }));
+  segment_selector_->set_segment_result_provider_for_testing(
+      std::move(result_provider));
+
+  clock_.Advance(base::Days(1));
+  base::RunLoop wait_for_selection;
+  segment_selector_->GetSelectedSegmentOnDemand(base::BindOnce(
+      [](base::OnceClosure quit, const SegmentSelectionResult& result) {
+        EXPECT_TRUE(result.is_ready);
+        EXPECT_EQ(kSegmentId2, result.segment);
+        std::move(quit).Run();
+      },
+      wait_for_selection.QuitClosure()));
+  wait_for_selection.Run();
+}
+
 TEST_F(SegmentSelectorTest, NewSegmentResultOverridesThePreviousBest) {
   Config config = CreateTestConfig();
   config.unknown_selection_ttl = base::TimeDelta();
diff --git a/components/segmentation_platform/internal/service_proxy_impl.cc b/components/segmentation_platform/internal/service_proxy_impl.cc
index ae6d0470..4aab9ffc 100644
--- a/components/segmentation_platform/internal/service_proxy_impl.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl.cc
@@ -11,8 +11,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/scheduler/execution_service.h"
 #include "components/segmentation_platform/internal/segmentation_platform_service_impl.h"
 #include "components/segmentation_platform/internal/selection/segment_selector_impl.h"
diff --git a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
index 18f3e3d..9e2aeb6e 100644
--- a/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
+++ b/components/segmentation_platform/internal/service_proxy_impl_unittest.cc
@@ -204,7 +204,8 @@
   auto scheduler_moved = std::make_unique<MockModelExecutionScheduler>();
   MockModelExecutionScheduler* scheduler = scheduler_moved.get();
   ExecutionService execution;
-  execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved));
+  execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved),
+                           nullptr);
 
   // Scheduler is not set, ExecuteModel() will do nothing.
   EXPECT_CALL(*scheduler, RequestModelExecution(_)).Times(0);
@@ -241,7 +242,8 @@
   auto scheduler_moved = std::make_unique<MockModelExecutionScheduler>();
   MockModelExecutionScheduler* scheduler = scheduler_moved.get();
   ExecutionService execution;
-  execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved));
+  execution.InitForTesting(nullptr, nullptr, std::move(scheduler_moved),
+                           nullptr);
 
   // Scheduler is not set, OverwriteValue() will do nothing.
   EXPECT_CALL(*scheduler, OnModelExecutionCompleted(_, _)).Times(0);
diff --git a/components/segmentation_platform/internal/signals/signal_filter_processor.cc b/components/segmentation_platform/internal/signals/signal_filter_processor.cc
index a7cc4cfa..6ddb2c36 100644
--- a/components/segmentation_platform/internal/signals/signal_filter_processor.cc
+++ b/components/segmentation_platform/internal/signals/signal_filter_processor.cc
@@ -7,11 +7,11 @@
 #include <set>
 
 #include "base/logging.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/database/segment_info_database.h"
 #include "components/segmentation_platform/internal/database/signal_storage_config.h"
 #include "components/segmentation_platform/internal/database/storage_service.h"
 #include "components/segmentation_platform/internal/execution/default_model_manager.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/model_prediction.pb.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
diff --git a/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc b/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
index d157db8..d895da33 100644
--- a/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
+++ b/components/segmentation_platform/internal/signals/signal_filter_processor_unittest.cc
@@ -35,11 +35,11 @@
 namespace segmentation_platform {
 namespace {
 
-UkmEventHash TestEvent(uint64_t val) {
+constexpr UkmEventHash TestEvent(uint64_t val) {
   return UkmEventHash::FromUnsafeValue(val);
 }
 
-UkmMetricHash TestMetric(uint64_t val) {
+constexpr UkmMetricHash TestMetric(uint64_t val) {
   return UkmMetricHash::FromUnsafeValue(val);
 }
 
@@ -197,6 +197,33 @@
 TEST_F(SignalFilterProcessorTest, UkmMetricsConfig) {
   const OptimizationTarget kSegmentId =
       OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
+  const UkmEventHash kEvent1 = TestEvent(10);
+  const UkmEventHash kEvent2 = TestEvent(11);
+  const std::array<UkmMetricHash, 3> kMetrics1_1{
+      TestMetric(100), TestMetric(101), TestMetric(102)};
+  const std::array<UkmMetricHash, 3> kMetrics1_2{
+      TestMetric(100), TestMetric(104), TestMetric(105)};
+  const std::array<UkmMetricHash, 3> kMetrics2{TestMetric(103), TestMetric(104),
+                                               TestMetric(105)};
+  const std::array<MetadataWriter::SqlFeature::EventAndMetrics, 2> kConfig1{
+      MetadataWriter::SqlFeature::EventAndMetrics{
+          .event_hash = kEvent1,
+          .metrics = kMetrics1_1.data(),
+          .metrics_size = kMetrics1_1.size()},
+      MetadataWriter::SqlFeature::EventAndMetrics{
+          .event_hash = kEvent2,
+          .metrics = kMetrics2.data(),
+          .metrics_size = kMetrics2.size()}};
+  const std::array<MetadataWriter::SqlFeature::EventAndMetrics, 1> kConfig2{
+      MetadataWriter::SqlFeature::EventAndMetrics{
+          .event_hash = kEvent1,
+          .metrics = kMetrics1_2.data(),
+          .metrics_size = kMetrics1_2.size()}};
+  MetadataWriter::SqlFeature feature1{
+      .sql = "", .events = kConfig1.data(), .events_size = kConfig1.size()};
+  MetadataWriter::SqlFeature feature2{
+      .sql = "", .events = kConfig2.data(), .events_size = kConfig2.size()};
+
   EXPECT_CALL(*histogram_signal_handler_,
               SetRelevantHistograms(
                   std::set<std::pair<std::string, proto::SignalType>>()))
@@ -214,15 +241,16 @@
   signal_filter_processor_->OnSignalListUpdated();
 
   UkmConfig config1;
-  config1.AddEvent(TestEvent(10),
-                   {TestMetric(100), TestMetric(101), TestMetric(102)});
-  config1.AddEvent(TestEvent(11),
-                   {TestMetric(103), TestMetric(104), TestMetric(105)});
-  segment_database_->AddSqlFeature(kSegmentId, "", config1);
+  config1.AddEvent(kEvent1, base::flat_set<UkmMetricHash>(kMetrics1_1.begin(),
+                                                          kMetrics1_1.end()));
+  config1.AddEvent(kEvent2, base::flat_set<UkmMetricHash>(kMetrics2.begin(),
+                                                          kMetrics2.end()));
+  segment_database_->AddSqlFeature(kSegmentId, feature1);
+
   UkmConfig config2;
-  config2.AddEvent(TestEvent(10),
-                   {TestMetric(100), TestMetric(104), TestMetric(105)});
-  segment_database_->AddSqlFeature(kSegmentId, "", config2);
+  config2.AddEvent(kEvent1, base::flat_set<UkmMetricHash>(kMetrics1_2.begin(),
+                                                          kMetrics1_2.end()));
+  segment_database_->AddSqlFeature(kSegmentId, feature2);
 
   config2.Merge(config1);
 
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index 6e482a47..3a468da 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -6,8 +6,8 @@
 #define COMPONENTS_SEGMENTATION_PLATFORM_INTERNAL_STATS_H_
 
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
+#include "components/segmentation_platform/internal/metadata/metadata_utils.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -170,7 +170,8 @@
   kAtLeastOneSegmentDefaultSignalNotCollected = 12,
   kAtLeastOneSegmentDefaultExecFailed = 13,
   kAtLeastOneSegmentDefaultMissingMetadata = 14,
-  kMaxValue = kAtLeastOneSegmentDefaultMissingMetadata
+  kAtLeastOneSegmentTfliteExecFailed = 15,
+  kMaxValue = kAtLeastOneSegmentTfliteExecFailed
 };
 
 // Records the reason for failure or success to compute a segment selection.
diff --git a/components/segmentation_platform/public/config.h b/components/segmentation_platform/public/config.h
index 8d649bf8..79a58bc4 100644
--- a/components/segmentation_platform/public/config.h
+++ b/components/segmentation_platform/public/config.h
@@ -70,6 +70,11 @@
 
   // List of segment ids that the current config requires to be available.
   std::vector<optimization_guide::proto::OptimizationTarget> segment_ids;
+
+  // The selection only supports returning results from on-demand model
+  // executions instead of returning result from previous sessions. The
+  // selection TTLs are ignored in this config.
+  bool on_demand_execution = false;
 };
 
 }  // namespace segmentation_platform
diff --git a/components/shared_highlighting/core/common/shared_highlighting_features.cc b/components/shared_highlighting/core/common/shared_highlighting_features.cc
index a46aced..0eaa4f4 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_features.cc
+++ b/components/shared_highlighting/core/common/shared_highlighting_features.cc
@@ -29,6 +29,17 @@
 const base::Feature kSharedHighlightingRefinedBlocklist{
     "SharedHighlightingRefinedBlocklist", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSharedHighlightingRefinedMaxContextWords{
+    "SharedHighlightingRefinedMaxContextWords",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
+const char kSharedHighlightingRefinedMaxContextWordsName[] =
+    "SharedHighlightingRefinedMaxContextWords";
+
+const base::FeatureParam<int> kSharedHighlightingMaxContextWords{
+    &kSharedHighlightingRefinedMaxContextWords,
+    kSharedHighlightingRefinedMaxContextWordsName, 10};
+
 int GetPreemptiveLinkGenTimeoutLengthMs() {
   return kPreemptiveLinkGenTimeoutLengthMs.Get();
 }
diff --git a/components/shared_highlighting/core/common/shared_highlighting_features.h b/components/shared_highlighting/core/common/shared_highlighting_features.h
index fbaf89be..9ed78fee 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_features.h
+++ b/components/shared_highlighting/core/common/shared_highlighting_features.h
@@ -27,6 +27,12 @@
 // Feature flag that enables a narrower blocklist.
 extern const base::Feature kSharedHighlightingRefinedBlocklist;
 
+// Feature flag that allows to experiment with different Max Context Words.
+extern const base::Feature kSharedHighlightingRefinedMaxContextWords;
+// Feature name and parameter to capture the different maxContextWords values.
+extern const char kSharedHighlightingRefinedMaxContextWordsName[];
+extern const base::FeatureParam<int> kSharedHighlightingMaxContextWords;
+
 // Returns the pre-emptive link generation timeout length.
 int GetPreemptiveLinkGenTimeoutLengthMs();
 
diff --git a/components/user_education/views/help_bubble_delegate.h b/components/user_education/views/help_bubble_delegate.h
index abe4dd7..d417282 100644
--- a/components/user_education/views/help_bubble_delegate.h
+++ b/components/user_education/views/help_bubble_delegate.h
@@ -40,13 +40,12 @@
 
   // These methods return color codes that will be handled by the app's theming
   // system.
-  virtual int GetHelpBubbleBackgroundColor() const = 0;
-  virtual int GetHelpBubbleForegroundColor() const = 0;
-  virtual int GetHelpBubbleDefaultButtonBackgroundColor() const = 0;
-  virtual int GetHelpBubbleDefaultButtonForegroundColor() const = 0;
-  virtual int GetHelpBubbleButtonBorderColor() const = 0;
-  virtual int GetHelpBubbleCloseButtonInkDropColor() const = 0;
   virtual ui::ColorId GetHelpBubbleBackgroundColorId() const = 0;
+  virtual ui::ColorId GetHelpBubbleForegroundColorId() const = 0;
+  virtual ui::ColorId GetHelpBubbleDefaultButtonBackgroundColorId() const = 0;
+  virtual ui::ColorId GetHelpBubbleDefaultButtonForegroundColorId() const = 0;
+  virtual ui::ColorId GetHelpBubbleButtonBorderColorId() const = 0;
+  virtual ui::ColorId GetHelpBubbleCloseButtonInkDropColorId() const = 0;
 };
 
 }  // namespace user_education
diff --git a/components/user_education/views/help_bubble_view.cc b/components/user_education/views/help_bubble_view.cc
index ce457631..bea56fab 100644
--- a/components/user_education/views/help_bubble_view.cc
+++ b/components/user_education/views/help_bubble_view.cc
@@ -26,7 +26,7 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/image_model.h"
-#include "ui/base/theme_provider.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
@@ -136,21 +136,21 @@
     // Prominent MD button does not have a border.
     // Override this method to draw a border.
     // Adapted from MdTextButton::UpdateBackgroundColor()
-    const auto* theme_provider = GetThemeProvider();
-    if (!theme_provider)
+    const auto* color_provider = GetColorProvider();
+    if (!color_provider)
       return;
-    SkColor background_color = theme_provider->GetColor(
+    SkColor background_color = color_provider->GetColor(
         is_default_button_
-            ? delegate_->GetHelpBubbleDefaultButtonBackgroundColor()
-            : delegate_->GetHelpBubbleBackgroundColor());
+            ? delegate_->GetHelpBubbleDefaultButtonBackgroundColorId()
+            : delegate_->GetHelpBubbleBackgroundColorId());
     if (GetState() == STATE_PRESSED) {
       background_color =
           GetNativeTheme()->GetSystemButtonPressedColor(background_color);
     }
-    const SkColor stroke_color = theme_provider->GetColor(
+    const SkColor stroke_color = color_provider->GetColor(
         is_default_button_
-            ? delegate_->GetHelpBubbleDefaultButtonBackgroundColor()
-            : delegate_->GetHelpBubbleButtonBorderColor());
+            ? delegate_->GetHelpBubbleDefaultButtonBackgroundColorId()
+            : delegate_->GetHelpBubbleButtonBorderColorId());
     SetBackground(CreateBackgroundFromPainter(
         views::Painter::CreateRoundRectWith1PxBorderPainter(
             background_color, stroke_color, GetCornerRadius())));
@@ -159,15 +159,15 @@
   void OnThemeChanged() override {
     views::MdTextButton::OnThemeChanged();
 
-    const auto* theme_provider = GetThemeProvider();
+    const auto* color_provider = GetColorProvider();
     const SkColor background_color =
-        theme_provider->GetColor(delegate_->GetHelpBubbleBackgroundColor());
+        color_provider->GetColor(delegate_->GetHelpBubbleBackgroundColorId());
     views::FocusRing::Get(this)->SetColor(background_color);
 
-    const SkColor foreground_color = theme_provider->GetColor(
+    const SkColor foreground_color = color_provider->GetColor(
         is_default_button_
-            ? delegate_->GetHelpBubbleDefaultButtonForegroundColor()
-            : delegate_->GetHelpBubbleForegroundColor());
+            ? delegate_->GetHelpBubbleDefaultButtonForegroundColorId()
+            : delegate_->GetHelpBubbleForegroundColorId());
     SetEnabledTextColors(foreground_color);
 
     // TODO(crbug/1112244): Temporary fix for Mac. Bubble shouldn't be in
@@ -199,25 +199,25 @@
         this,
         std::make_unique<views::CircleHighlightPathGenerator>(gfx::Insets()));
     SetAccessibleName(accessible_name);
+
+    constexpr int kIconSize = 16;
+    SetImageModel(views::ImageButton::STATE_NORMAL,
+                  ui::ImageModel::FromVectorIcon(
+                      views::kIcCloseIcon,
+                      delegate_->GetHelpBubbleForegroundColorId(), kIconSize));
+
+    constexpr float kCloseButtonFocusRingHaloThickness = 1.25f;
+    views::FocusRing::Get(this)->SetHaloThickness(
+        kCloseButtonFocusRingHaloThickness);
   }
 
   void OnThemeChanged() override {
     views::ImageButton::OnThemeChanged();
-
-    constexpr float kCloseButtonFocusRingHaloThickness = 1.25f;
-
-    const auto* theme_provider = GetThemeProvider();
-    SetImage(
-        views::ImageButton::STATE_NORMAL,
-        gfx::CreateVectorIcon(views::kIcCloseIcon, 16,
-                              theme_provider->GetColor(
-                                  delegate_->GetHelpBubbleForegroundColor())));
-    views::InkDrop::Get(this)->SetBaseColor(theme_provider->GetColor(
-        delegate_->GetHelpBubbleCloseButtonInkDropColor()));
+    const auto* color_provider = GetColorProvider();
+    views::InkDrop::Get(this)->SetBaseColor(color_provider->GetColor(
+        delegate_->GetHelpBubbleCloseButtonInkDropColorId()));
     views::FocusRing::Get(this)->SetColor(
-        theme_provider->GetColor(delegate_->GetHelpBubbleForegroundColor()));
-    views::FocusRing::Get(this)->SetHaloThickness(
-        kCloseButtonFocusRingHaloThickness);
+        color_provider->GetColor(delegate_->GetHelpBubbleForegroundColorId()));
   }
 
  private:
@@ -253,8 +253,8 @@
     const gfx::PointF center_point = local_bounds.CenterPoint();
     const float radius = (size_.width() - kStrokeWidth) / 2.0f;
 
-    const SkColor color =
-        GetThemeProvider()->GetColor(delegate_->GetHelpBubbleForegroundColor());
+    const SkColor color = GetColorProvider()->GetColor(
+        delegate_->GetHelpBubbleForegroundColorId());
     if (should_fill_) {
       cc::PaintFlags fill_flags;
       fill_flags.setStyle(cc::PaintFlags::kFill_Style);
@@ -655,13 +655,13 @@
 void HelpBubbleView::OnThemeChanged() {
   views::BubbleDialogDelegateView::OnThemeChanged();
 
-  const auto* theme_provider = GetThemeProvider();
+  const auto* color_provider = GetColorProvider();
   const SkColor background_color =
-      theme_provider->GetColor(delegate_->GetHelpBubbleBackgroundColor());
+      color_provider->GetColor(delegate_->GetHelpBubbleBackgroundColorId());
   set_color(background_color);
 
   const SkColor foreground_color =
-      theme_provider->GetColor(delegate_->GetHelpBubbleForegroundColor());
+      color_provider->GetColor(delegate_->GetHelpBubbleForegroundColorId());
   if (icon_view_) {
     icon_view_->SetBackground(views::CreateRoundedRectBackground(
         foreground_color, icon_view_->GetPreferredSize().height() / 2));
diff --git a/components/user_education/views/help_bubble_view_unittest.cc b/components/user_education/views/help_bubble_view_unittest.cc
index 12df66c..8ed909a 100644
--- a/components/user_education/views/help_bubble_view_unittest.cc
+++ b/components/user_education/views/help_bubble_view_unittest.cc
@@ -50,13 +50,18 @@
 
   // These methods return color codes that will be handled by the app's theming
   // system.
-  int GetHelpBubbleBackgroundColor() const override { return 0; }
-  int GetHelpBubbleForegroundColor() const override { return 0; }
-  int GetHelpBubbleDefaultButtonBackgroundColor() const override { return 0; }
-  int GetHelpBubbleDefaultButtonForegroundColor() const override { return 0; }
-  int GetHelpBubbleButtonBorderColor() const override { return 0; }
-  int GetHelpBubbleCloseButtonInkDropColor() const override { return 0; }
   ui::ColorId GetHelpBubbleBackgroundColorId() const override { return 0; }
+  ui::ColorId GetHelpBubbleForegroundColorId() const override { return 0; }
+  ui::ColorId GetHelpBubbleDefaultButtonBackgroundColorId() const override {
+    return 0;
+  }
+  ui::ColorId GetHelpBubbleDefaultButtonForegroundColorId() const override {
+    return 0;
+  }
+  ui::ColorId GetHelpBubbleButtonBorderColorId() const override { return 0; }
+  ui::ColorId GetHelpBubbleCloseButtonInkDropColorId() const override {
+    return 0;
+  }
 };
 
 // Fake theme provider. There's a similar TestThemeProvider in chrome/test but
diff --git a/components/viz/service/display/overlay_processor_ozone.cc b/components/viz/service/display/overlay_processor_ozone.cc
index 828be28..1c83da9 100644
--- a/components/viz/service/display/overlay_processor_ozone.cc
+++ b/components/viz/service/display/overlay_processor_ozone.cc
@@ -71,13 +71,6 @@
          (mailbox.name[2] << 8) + mailbox.name[3];
 }
 
-void ReportSharedImageExists(bool exists) {
-  UMA_HISTOGRAM_BOOLEAN(
-      "Compositing.Display.OverlayProcessorOzone."
-      "SharedImageExists",
-      exists);
-}
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 bool AllowColorSpaceCombination(
     const gfx::ColorSpace& source_color_space,
@@ -305,11 +298,6 @@
     bool is_primary) {
   DCHECK(shared_image_interface_);
 
-  UMA_HISTOGRAM_BOOLEAN(
-      "Compositing.Display.OverlayProcessorOzone."
-      "IsCandidateSharedImage",
-      mailbox.IsSharedImage());
-
   if (!mailbox.IsSharedImage())
     return false;
 
@@ -323,10 +311,8 @@
     // candidate. We will try again next frame.
     DLOG(ERROR) << "Unable to find the NativePixmap corresponding to the "
                    "overlay candidate";
-    ReportSharedImageExists(false);
     return false;
   }
-  ReportSharedImageExists(true);
 
   if (is_primary && (candidate->buffer_size != native_pixmap->GetBufferSize() ||
                      candidate->format != native_pixmap->GetBufferFormat())) {
diff --git a/content/BUILD.gn b/content/BUILD.gn
index e0b59ac..8e616f8 100644
--- a/content/BUILD.gn
+++ b/content/BUILD.gn
@@ -116,6 +116,7 @@
     "//content/browser/prerender:mojo_bindings_webui_js",
     "//content/browser/process_internals:mojo_bindings_webui_js",
     "//content/browser/resources/attribution_reporting:build_ts",
+    "//content/browser/resources/gpu:web_components",
     "//storage/browser/quota:mojo_bindings_webui_js",
   ]
 }
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index 489f429..a07be30a 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -74,15 +74,19 @@
   WebUIDataSource* source = WebUIDataSource::Create(kChromeUIGpuHost);
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ScriptSrc,
-      "script-src chrome://resources 'self' 'unsafe-eval';");
+      "script-src chrome://resources 'self';");
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::TrustedTypes,
-      "trusted-types jstemplate;");
+      "trusted-types static-types;");
 
   source->UseStringsJs();
   source->AddResourcePath("browser_bridge.js", IDR_GPU_BROWSER_BRIDGE_JS);
   source->AddResourcePath("gpu_internals.js", IDR_GPU_INTERNALS_JS);
-  source->AddResourcePath("info_view.js", IDR_GPU_INFO_VIEW_JS);
+  source->AddResourcePath("info_view.js", IDR_GPU_INTERNALS_INFO_VIEW_JS);
+  source->AddResourcePath("info_view_table.js",
+                          IDR_GPU_INTERNALS_INFO_VIEW_TABLE_JS);
+  source->AddResourcePath("info_view_table_row.js",
+                          IDR_GPU_INTERNALS_INFO_VIEW_TABLE_ROW_JS);
   source->AddResourcePath("vulkan_info.js", IDR_GPU_VULKAN_INFO_JS);
   source->AddResourcePath("vulkan_info.mojom-webui.js",
                           IDR_VULKAN_INFO_MOJO_JS);
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index e484380..a83340a 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -2517,17 +2517,17 @@
       TestDevToolsAgentClient::Event breakpoint_hit =
           debug.WaitForMethodNotification("Debugger.paused");
 
-      base::Value* hit_breakpoints =
-          breakpoint_hit.value.FindListPath("params.hitBreakpoints");
+      ASSERT_TRUE(breakpoint_hit.value.is_dict());
+      base::Value::List* hit_breakpoints =
+          breakpoint_hit.value.GetDict().FindListByDottedPath(
+              "params.hitBreakpoints");
       ASSERT_TRUE(hit_breakpoints);
-      base::Value::ConstListView hit_breakpoints_list =
-          hit_breakpoints->GetListDeprecated();
       // This is LE and not EQ to work around
       // https://bugs.chromium.org/p/v8/issues/detail?id=12586
-      ASSERT_LE(1u, hit_breakpoints_list.size());
-      ASSERT_TRUE(hit_breakpoints_list[0].is_string());
+      ASSERT_LE(1u, hit_breakpoints->size());
+      ASSERT_TRUE((*hit_breakpoints)[0].is_string());
       EXPECT_EQ(base::StringPrintf("1:11:0:%s", debug_url.spec().c_str()),
-                hit_breakpoints_list[0].GetString());
+                (*hit_breakpoints)[0].GetString());
 
       // Just resume execution.
       debug.RunCommandAndWaitForResult(
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index c87fb52..31e81717 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -120,18 +120,20 @@
 }
 
 base::Value ToValue(const blink::InterestGroup::Ad& ad) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetStringKey("url", ad.render_url.spec());
+  base::Value value(base::Value::Type::DICTIONARY);
+  base::Value::Dict& dict = value.GetDict();
+  dict.Set("url", ad.render_url.spec());
   if (ad.metadata)
-    dict.SetStringKey("metadata", ad.metadata.value());
-  return dict;
+    dict.Set("metadata", ad.metadata.value());
+  return value;
 }
-blink::InterestGroup::Ad FromInterestGroupAdValue(const base::Value* value) {
+blink::InterestGroup::Ad FromInterestGroupAdValue(
+    const base::Value::Dict& dict) {
   blink::InterestGroup::Ad result;
-  const std::string* maybe_url = value->FindStringKey("url");
+  const std::string* maybe_url = dict.FindString("url");
   if (maybe_url)
     result.render_url = GURL(*maybe_url);
-  const std::string* maybe_metadata = value->FindStringKey("metadata");
+  const std::string* maybe_metadata = dict.FindString("metadata");
   if (maybe_metadata)
     result.metadata = *maybe_metadata;
   return result;
@@ -153,8 +155,10 @@
   if (!ads_value || !ads_value->is_list())
     return absl::nullopt;
   std::vector<blink::InterestGroup::Ad> result;
-  for (const auto& ad_value : ads_value->GetListDeprecated()) {
-    result.emplace_back(FromInterestGroupAdValue(&ad_value));
+  for (const auto& ad_value : ads_value->GetList()) {
+    const base::Value::Dict* dict = ad_value.GetIfDict();
+    if (dict)
+      result.emplace_back(FromInterestGroupAdValue(*dict));
   }
   return result;
 }
diff --git a/content/browser/interest_group/interest_group_update_manager.cc b/content/browser/interest_group/interest_group_update_manager.cc
index c285354..327866f 100644
--- a/content/browser/interest_group/interest_group_update_manager.cc
+++ b/content/browser/interest_group/interest_group_update_manager.cc
@@ -79,16 +79,17 @@
 // TODO(crbug.com/1186444): Report errors to devtools for the TryToCopy*().
 // functions.
 
-// Name and owner are optional in `value` (parsed server JSON response), but
+// Name and owner are optional in `dict` (parsed server JSON response), but
 // must match `name` and `owner`, respectively, if either is specified. Returns
 // true if the check passes, and false otherwise.
-[[nodiscard]] bool ValidateNameAndOwnerIfPresent(const url::Origin& owner,
-                                                 const std::string& name,
-                                                 const base::Value& value) {
-  const std::string* maybe_owner = value.FindStringKey("owner");
+[[nodiscard]] bool ValidateNameAndOwnerIfPresent(
+    const url::Origin& owner,
+    const std::string& name,
+    const base::Value::Dict& dict) {
+  const std::string* maybe_owner = dict.FindString("owner");
   if (maybe_owner && url::Origin::Create(GURL(*maybe_owner)) != owner)
     return false;
-  const std::string* maybe_name = value.FindStringKey("name");
+  const std::string* maybe_name = dict.FindString("name");
   if (maybe_name && *maybe_name != name)
     return false;
   return true;
@@ -99,14 +100,14 @@
 // completed.
 [[nodiscard]] bool TryToCopyTrustedBiddingSignalsKeys(
     blink::InterestGroup& interest_group_update,
-    const base::Value& value) {
-  const base::Value* maybe_update_trusted_bidding_signals_keys =
-      value.FindListKey("trustedBiddingSignalsKeys");
+    const base::Value::Dict& dict) {
+  const base::Value::List* maybe_update_trusted_bidding_signals_keys =
+      dict.FindList("trustedBiddingSignalsKeys");
   if (!maybe_update_trusted_bidding_signals_keys)
     return true;
   std::vector<std::string> trusted_bidding_signals_keys;
   for (const base::Value& keys_value :
-       maybe_update_trusted_bidding_signals_keys->GetListDeprecated()) {
+       *maybe_update_trusted_bidding_signals_keys) {
     const std::string* maybe_key = keys_value.GetIfString();
     if (!maybe_key)
       return false;
@@ -119,17 +120,18 @@
 
 // Helper for TryToCopyAds() and TryToCopyAdComponents().
 [[nodiscard]] absl::optional<std::vector<blink::InterestGroup::Ad>> ExtractAds(
-    const base::Value& ads_list) {
+    const base::Value::List& ads_list) {
   std::vector<blink::InterestGroup::Ad> ads;
-  for (const base::Value& ads_value : ads_list.GetListDeprecated()) {
-    if (!ads_value.is_dict())
+  for (const base::Value& ads_value : ads_list) {
+    const base::Value::Dict* ads_dict = ads_value.GetIfDict();
+    if (!ads_dict)
       return absl::nullopt;
-    const std::string* maybe_render_url = ads_value.FindStringKey("renderUrl");
+    const std::string* maybe_render_url = ads_dict->FindString("renderUrl");
     if (!maybe_render_url)
       return absl::nullopt;
     blink::InterestGroup::Ad ad;
     ad.render_url = GURL(*maybe_render_url);
-    const base::Value* maybe_metadata = ads_value.FindKey("metadata");
+    const base::Value* maybe_metadata = ads_dict->Find("metadata");
     if (maybe_metadata) {
       std::string metadata;
       JSONStringValueSerializer serializer(&metadata);
@@ -148,8 +150,8 @@
 // Copies the `ads` list JSON field into `interest_group_update`, returns true
 // iff the JSON is valid and the copy completed.
 [[nodiscard]] bool TryToCopyAds(blink::InterestGroup& interest_group_update,
-                                const base::Value& value) {
-  const base::Value* maybe_ads = value.FindListKey("ads");
+                                const base::Value::Dict& dict) {
+  const base::Value::List* maybe_ads = dict.FindList("ads");
   if (!maybe_ads)
     return true;
   absl::optional<std::vector<blink::InterestGroup::Ad>> maybe_extracted_ads =
@@ -164,8 +166,8 @@
 // returns true iff the JSON is valid and the copy completed.
 [[nodiscard]] bool TryToCopyAdComponents(
     blink::InterestGroup& interest_group_update,
-    const base::Value& value) {
-  const base::Value* maybe_ads = value.FindListKey("adComponents");
+    const base::Value::Dict& dict) {
+  const base::Value::List* maybe_ads = dict.FindList("adComponents");
   if (!maybe_ads)
     return true;
   absl::optional<std::vector<blink::InterestGroup::Ad>> maybe_extracted_ads =
@@ -184,45 +186,45 @@
   if (result.error) {
     return absl::nullopt;
   }
-  const base::Value& value = *result.value;
-  if (!value.is_dict()) {
+  const base::Value::Dict* dict = result.value->GetIfDict();
+  if (!dict) {
     return absl::nullopt;
   }
-  if (!ValidateNameAndOwnerIfPresent(owner, name, value)) {
+  if (!ValidateNameAndOwnerIfPresent(owner, name, *dict)) {
     return absl::nullopt;
   }
   blink::InterestGroup interest_group_update;
   interest_group_update.owner = owner;
   interest_group_update.name = name;
-  const base::Value* maybe_priority_value = value.FindKey("priority");
+  const base::Value* maybe_priority_value = dict->Find("priority");
   if (maybe_priority_value) {
     // If the field is specified, it must be an integer or a double.
     if (!maybe_priority_value->is_int() && !maybe_priority_value->is_double())
       return absl::nullopt;
     interest_group_update.priority = maybe_priority_value->GetDouble();
   }
-  const std::string* maybe_bidding_url = value.FindStringKey("biddingLogicUrl");
+  const std::string* maybe_bidding_url = dict->FindString("biddingLogicUrl");
   if (maybe_bidding_url)
     interest_group_update.bidding_url = GURL(*maybe_bidding_url);
   const std::string* maybe_bidding_wasm_helper_url =
-      value.FindStringKey("biddingWasmHelperUrl");
+      dict->FindString("biddingWasmHelperUrl");
   if (maybe_bidding_wasm_helper_url) {
     interest_group_update.bidding_wasm_helper_url =
         GURL(*maybe_bidding_wasm_helper_url);
   }
   const std::string* maybe_update_trusted_bidding_signals_url =
-      value.FindStringKey("trustedBiddingSignalsUrl");
+      dict->FindString("trustedBiddingSignalsUrl");
   if (maybe_update_trusted_bidding_signals_url) {
     interest_group_update.trusted_bidding_signals_url =
         GURL(*maybe_update_trusted_bidding_signals_url);
   }
-  if (!TryToCopyTrustedBiddingSignalsKeys(interest_group_update, value)) {
+  if (!TryToCopyTrustedBiddingSignalsKeys(interest_group_update, *dict)) {
     return absl::nullopt;
   }
-  if (!TryToCopyAds(interest_group_update, value)) {
+  if (!TryToCopyAds(interest_group_update, *dict)) {
     return absl::nullopt;
   }
-  if (!TryToCopyAdComponents(interest_group_update, value)) {
+  if (!TryToCopyAdComponents(interest_group_update, *dict)) {
     return absl::nullopt;
   }
   if (!interest_group_update.IsValid()) {
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index 13a8021..a26874ce4 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -698,7 +698,7 @@
     TestNavigationObserver navigation_observer(a_url);
     navigation_observer.StartWatchingNewWebContents();
     EXPECT_TRUE(ExecJs(
-        main_frame, JsReplace("var portal = document.createElement('portal');"
+        main_frame, JsReplace("let portal = document.createElement('portal');"
                               "portal.src = $1;"
                               "portal.style.width = '500px';"
                               "portal.style.height = '500px';"
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
index a4989f1b..bab2c910 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc
@@ -136,8 +136,6 @@
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::OnFindFamily");
   UINT32 family_index = UINT32_MAX;
   if (collection_) {
-    SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-        "DirectWrite.Fonts.Content.FindFamilyTime");
     BOOL exists = FALSE;
     UINT32 index = UINT32_MAX;
     HRESULT hr = collection_->FindFamilyName(base::as_wcstr(family_name),
@@ -153,8 +151,6 @@
 void DWriteFontProxyImpl::GetFamilyCount(GetFamilyCountCallback callback) {
   InitializeDirectWrite();
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::OnGetFamilyCount");
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-      "DirectWrite.Fonts.Content.GetFamilyCountTime");
   std::move(callback).Run(collection_ ? collection_->GetFontFamilyCount() : 0);
 }
 
@@ -167,8 +163,6 @@
   if (!collection_)
     return;
 
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-      "DirectWrite.Fonts.Content.GetFamilyNamesTime");
   TRACE_EVENT0("dwrite,fonts", "FontProxyHost::DoGetFamilyNames");
 
   mswr::ComPtr<IDWriteFontFamily> family;
@@ -231,8 +225,6 @@
   if (!collection_)
     return;
 
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-      "DirectWrite.Fonts.Content.GetFontFilesTime");
   mswr::ComPtr<IDWriteFontFamily> family;
   HRESULT hr = collection_->GetFontFamily(family_index, &family);
   if (FAILED(hr)) {
@@ -319,8 +311,6 @@
     }
   }
 
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-      "DirectWrite.Fonts.Content.MapCharactersTime");
   mswr::ComPtr<IDWriteFont> mapped_font;
 
   mswr::ComPtr<IDWriteNumberSubstitution> number_substitution;
@@ -530,8 +520,6 @@
   if (!codepoint || !collection_ || !factory_)
     return;
 
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
-      "DirectWrite.Fonts.Content.FallbackFamilyAndStyleForCodepointTime");
   sk_sp<SkFontMgr> font_mgr(
       SkFontMgr_New_DirectWrite(factory_.Get(), collection_.Get()));
 
@@ -563,7 +551,6 @@
 void DWriteFontProxyImpl::InitializeDirectWrite() {
   if (direct_write_initialized_)
     return;
-  SCOPED_UMA_HISTOGRAM_TIMER_MICROS("DirectWrite.Fonts.Content.InitializeTime");
   direct_write_initialized_ = true;
 
   TRACE_EVENT0("dwrite,fonts", "DWriteFontProxyImpl::InitializeDirectWrite");
diff --git a/content/browser/renderer_host/input/fling_controller_unittest.cc b/content/browser/renderer_host/input/fling_controller_unittest.cc
index ebd445ad..81208f00 100644
--- a/content/browser/renderer_host/input/fling_controller_unittest.cc
+++ b/content/browser/renderer_host/input/fling_controller_unittest.cc
@@ -216,6 +216,7 @@
   bool first_wheel_event_sent_ = false;
   int sent_scroll_gesture_count_ = 0;
 #if BUILDFLAG(IS_WIN)
+  // This is necessary for static methods of `display::ScreenWin`.
   display::win::test::ScopedScreenWin scoped_screen_win_;
 #endif
 
diff --git a/content/browser/renderer_host/input/fling_scheduler_unittest.cc b/content/browser/renderer_host/input/fling_scheduler_unittest.cc
index f8de4972b..1b45630 100644
--- a/content/browser/renderer_host/input/fling_scheduler_unittest.cc
+++ b/content/browser/renderer_host/input/fling_scheduler_unittest.cc
@@ -15,10 +15,6 @@
 #include "content/test/test_render_widget_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "ui/display/win/test/scoped_screen_win.h"
-#endif
-
 namespace content {
 
 class FakeFlingScheduler : public FlingScheduler {
@@ -139,9 +135,6 @@
   scoped_refptr<SiteInstanceGroup> site_instance_group_;
   std::unique_ptr<TestRenderWidgetHostView> view_;
   std::unique_ptr<MockRenderWidgetHostDelegate> delegate_;
-#if BUILDFLAG(IS_WIN)
-  display::win::test::ScopedScreenWin scoped_screen_win_;
-#endif
 };
 
 TEST_F(FlingSchedulerTest, ScheduleNextFlingProgress) {
diff --git a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
index 292f17f..8ba2b0f7 100644
--- a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
@@ -25,10 +25,6 @@
 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
 #include "ui/events/blink/blink_features.h"
 
-#if BUILDFLAG(IS_WIN)
-#include "ui/display/win/test/scoped_screen_win.h"
-#endif
-
 using blink::WebGestureDevice;
 using blink::WebGestureEvent;
 using blink::WebInputEvent;
@@ -244,9 +240,6 @@
   std::unique_ptr<blink::mojom::InputEventResultState> sync_ack_result_;
   std::unique_ptr<WebGestureEvent> sync_followup_event_;
   base::test::ScopedFeatureList feature_list_;
-#if BUILDFLAG(IS_WIN)
-  display::win::test::ScopedScreenWin scoped_screen_win_;
-#endif
 };
 
 class GestureEventQueueWithCompositorEventQueueTest
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index b4e1505c..3e4d7c6 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -46,10 +46,6 @@
 #include "ui/events/event.h"
 #endif
 
-#if BUILDFLAG(IS_WIN)
-#include "ui/display/win/test/scoped_screen_win.h"
-#endif
-
 using blink::SyntheticWebGestureEventBuilder;
 using blink::SyntheticWebMouseEventBuilder;
 using blink::SyntheticWebMouseWheelEventBuilder;
@@ -970,9 +966,6 @@
 TEST_F(InputRouterImplTest, DISABLED_GestureTypesIgnoringAck) {
   // We test every gesture type, ensuring that the stream of gestures is valid.
 
-#if BUILDFLAG(IS_WIN)
-  display::win::test::ScopedScreenWin scoped_screen_win_;
-#endif
   const WebInputEvent::Type eventTypes[] = {
       WebInputEvent::Type::kGestureTapDown,
       WebInputEvent::Type::kGestureShowPress,
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index c983b5f..b41b617 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1101,6 +1101,24 @@
     std::move(callback).Run({});
 }
 
+// Creates a JavaScriptExecuteRequestForTestsCallback callback that delegates
+// to the given JavaScriptResultCallback.
+blink::mojom::LocalFrame::JavaScriptExecuteRequestForTestsCallback
+CreateJavaScriptExecuteRequestForTestsCallback(
+    RenderFrameHost::JavaScriptResultCallback callback) {
+  if (!callback)
+    return base::NullCallback();
+  return base::BindOnce(
+      [](RenderFrameHost::JavaScriptResultCallback callback,
+         blink::mojom::JavaScriptExecutionResultType type, base::Value value) {
+        if (type == blink::mojom::JavaScriptExecutionResultType::kSuccess)
+          std::move(callback).Run(value.Clone());
+        else
+          std::move(callback).Run(base::Value());
+      },
+      std::move(callback));
+}
+
 }  // namespace
 
 class RenderFrameHostImpl::SubresourceLoaderFactoriesConfig {
@@ -2483,34 +2501,42 @@
     const std::u16string& javascript,
     JavaScriptResultCallback callback,
     int32_t world_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  AssertNonSpeculativeFrame();
-
-  const bool has_user_gesture = false;
-  const bool wants_result = !callback.is_null();
-  GetAssociatedLocalFrame()->JavaScriptExecuteRequestForTests(  // IN-TEST
-      javascript, wants_result, has_user_gesture, world_id,
-      std::move(callback));
+  ExecuteJavaScriptForTests(  // IN-TEST
+      javascript, /*has_user_gesture=*/false,
+      /*resolve_promises=*/false, world_id,
+      CreateJavaScriptExecuteRequestForTestsCallback(std::move(callback)));
 }
 
 void RenderFrameHostImpl::ExecuteJavaScriptWithUserGestureForTests(
     const std::u16string& javascript,
     JavaScriptResultCallback callback,
     int32_t world_id) {
+  ExecuteJavaScriptForTests(  // IN-TEST
+      javascript, /*has_user_gesture=*/true,
+      /*resolve_promises=*/false, world_id,
+      CreateJavaScriptExecuteRequestForTestsCallback(std::move(callback)));
+}
+
+void RenderFrameHostImpl::ExecuteJavaScriptForTests(
+    const std::u16string& javascript,
+    bool has_user_gesture,
+    bool resolve_promises,
+    int32_t world_id,
+    JavaScriptResultAndTypeCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   AssertNonSpeculativeFrame();
 
-  // TODO(mustaq): The render-to-browser state update caused by the below
-  // JavaScriptExecuteRequestsForTests call is redundant with this update. We
-  // should determine if the redundancy can be removed.
-  frame_tree_node()->UpdateUserActivationState(
-      blink::mojom::UserActivationUpdateType::kNotifyActivation,
-      blink::mojom::UserActivationNotificationType::kTest);
+  if (has_user_gesture) {
+    // TODO(mustaq): The render-to-browser state update caused by the below
+    // JavaScriptExecuteRequestsForTests call is redundant with this update. We
+    // should determine if the redundancy can be removed.
+    frame_tree_node()->UpdateUserActivationState(
+        blink::mojom::UserActivationUpdateType::kNotifyActivation,
+        blink::mojom::UserActivationNotificationType::kTest);
+  }
 
-  const bool has_user_gesture = true;
-  const bool wants_result = !callback.is_null();
   GetAssociatedLocalFrame()->JavaScriptExecuteRequestForTests(  // IN-TEST
-      javascript, wants_result, has_user_gesture, world_id,
+      javascript, has_user_gesture, resolve_promises, world_id,
       std::move(callback));
 }
 
@@ -6210,25 +6236,31 @@
 void RenderFrameHostImpl::EnterFullscreen(
     blink::mojom::FullscreenOptionsPtr options,
     EnterFullscreenCallback callback) {
-  // Consume the user activation when entering fullscreen mode in the browser
-  // side when the renderer is compromised and the fullscreen request is denied.
-  // Fullscreen can only be triggered by: a user activation, a user-generated
-  // screen orientation change, or another feature-specific transient allowance.
+  const bool had_fullscreen_token = fullscreen_request_token_.IsActive();
+
+  // Entering fullscreen requires a transient user activation, a fullscreen
+  // capability delegation token, a user-generated screen orientation change, or
+  // another feature-specific transient allowance.
   // CanEnterFullscreenWithoutUserActivation is only ever true in tests, to
   // allow fullscreen when mocking screen orientation changes.
-  // TODO(lanwei): Investigate whether we can terminate the renderer when the
-  // user activation has already been consumed.
   if (!delegate_->HasSeenRecentScreenOrientationChange() &&
       !WindowPlacementAllowsFullscreen() && !HasSeenRecentXrOverlaySetup() &&
       !GetContentClient()
            ->browser()
            ->CanEnterFullscreenWithoutUserActivation()) {
-    bool is_consumed = frame_tree_node_->UpdateUserActivationState(
-        blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
-        blink::mojom::UserActivationNotificationType::kNone);
-    if (!is_consumed) {
+    // Consume any transient user activation and delegated fullscreen token.
+    // Reject requests made without transient user activation or a token.
+    // TODO(lanwei): Investigate whether we can terminate the renderer when
+    // transient user activation and the delegated token are both inactive.
+    const bool consumed_activation =
+        frame_tree_node_->UpdateUserActivationState(
+            blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
+            blink::mojom::UserActivationNotificationType::kNone);
+    const bool consumed_token = fullscreen_request_token_.ConsumeIfActive();
+    if (!consumed_activation && !consumed_token) {
       DLOG(ERROR) << "Cannot enter fullscreen because there is no transient "
-                  << "user activation, orientation change, or XR overlay.";
+                  << "user activation, orientation change, XR overlay, nor "
+                  << "capability delegation.";
       std::move(callback).Run(/*granted=*/false);
       return;
     }
@@ -6284,6 +6316,9 @@
     notified_instances.insert(parent_site_instance);
   }
 
+  // Focus the window if another frame may have delegated the capability.
+  if (had_fullscreen_token && !GetView()->HasFocus())
+    GetView()->Focus();
   delegate_->EnterFullscreenMode(this, *options);
   delegate_->FullscreenStateChanged(this, /*is_fullscreen=*/true,
                                     std::move(options));
@@ -7343,6 +7378,14 @@
   child->SetSrcdocValue(srcdoc_value);
 }
 
+void RenderFrameHostImpl::ReceivedDelegatedCapability(
+    blink::mojom::DelegatedCapability delegated_capability) {
+  if (delegated_capability ==
+      blink::mojom::DelegatedCapability::kFullscreenRequest) {
+    fullscreen_request_token_.Activate();
+  }
+}
+
 // TODO(ahemery): Move checks to mojo bad message reporting.
 void RenderFrameHostImpl::BeginNavigation(
     blink::mojom::CommonNavigationParamsPtr common_params,
@@ -11948,6 +11991,9 @@
     blink::TransferableMessage message) {
   DCHECK(is_render_frame_created());
 
+  if (message.delegated_capability != blink::mojom::DelegatedCapability::kNone)
+    ReceivedDelegatedCapability(message.delegated_capability);
+
   GetAssociatedLocalFrame()->PostMessageEvent(
       source_token, source_origin, target_origin, std::move(message));
 }
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index bd7e408..7acb4e3 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -101,6 +101,7 @@
 #include "services/network/public/mojom/url_loader_network_service_observer.mojom-forward.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
+#include "third_party/blink/public/common/frame/fullscreen_request_token.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
@@ -2149,6 +2150,8 @@
   void FrameSizeChanged(const gfx::Size& frame_size) override;
   void DidChangeSrcDoc(const blink::FrameToken& child_frame_token,
                        const std::string& srcdoc_value) override;
+  void ReceivedDelegatedCapability(
+      blink::mojom::DelegatedCapability delegated_capability) override;
 
   // blink::mojom::BackForwardCacheControllerHost:
   void EvictFromBackForwardCache(blink::mojom::RendererEvictionReason) override;
@@ -2486,6 +2489,19 @@
     kIframeNestedWithinFencedFrame
   };
 
+  using JavaScriptResultAndTypeCallback =
+      base::OnceCallback<void(blink::mojom::JavaScriptExecutionResultType,
+                              base::Value)>;
+
+  // Runs JavaScript in this frame, without restrictions. ONLY FOR TESTS.
+  // This method can optionally trigger a fake user activation notification,
+  // and can wait for returned promises to be resolved.
+  void ExecuteJavaScriptForTests(const std::u16string& javascript,
+                                 bool has_user_gesture,
+                                 bool resolve_promises,
+                                 int32_t world_id,
+                                 JavaScriptResultAndTypeCallback callback);
+
  protected:
   friend class RenderFrameHostFactory;
 
@@ -4228,6 +4244,9 @@
   // Manages a transient affordance for this frame or subframes to open a popup.
   TransientAllowPopup transient_allow_popup_;
 
+  // Manages a transient affordance for this frame to request fullscreen.
+  blink::FullscreenRequestToken fullscreen_request_token_;
+
   // Used to avoid sending AXTreeData to the renderer if the renderer has not
   // been told root ID yet. See UpdateAXTreeData() for more details.
   bool needs_ax_root_id_ = true;
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 2e82237e..d4d89d9 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -4387,7 +4387,9 @@
   // Execute script in an isolated world to avoid causing a Trusted Types
   // violation due to eval.
   EXPECT_EQ("Graphics Feature Status",
-            EvalJs(main_document, "document.querySelector('h3').textContent",
+            EvalJs(main_document,
+                   "document.querySelector('info-view').shadowRoot"
+                   ".querySelector('h3').textContent",
                    EXECUTE_SCRIPT_DEFAULT_OPTIONS, /*world_id=*/1));
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index b22ccbf..17c5ed84 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -81,6 +81,7 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "content/browser/renderer_host/test_render_widget_host_view_mac_factory.h"
+#include "ui/display/test/test_screen.h"
 #endif
 
 #if defined(USE_AURA) || BUILDFLAG(IS_MAC)
@@ -558,6 +559,10 @@
     // calls display::Screen::SetScreenInstance().
     ui::SetScreenAndroid(false /* use_display_wide_color_gamut */);
 #endif
+#if BUILDFLAG(IS_MAC)
+    screen_ = std::make_unique<display::test::TestScreen>();
+    display::Screen::SetScreenInstance(screen_.get());
+#endif
 #if defined(USE_AURA)
     screen_.reset(aura::TestScreen::Create(gfx::Size()));
     display::Screen::SetScreenInstance(screen_.get());
@@ -619,11 +624,11 @@
     browser_context_.reset();
 
 #if defined(USE_AURA)
-    display::Screen::SetScreenInstance(nullptr);
-    screen_.reset();
+    ImageTransportFactory::Terminate();
 #endif
 #if defined(USE_AURA) || BUILDFLAG(IS_MAC)
-    ImageTransportFactory::Terminate();
+    display::Screen::SetScreenInstance(nullptr);
+    screen_.reset();
 #endif
 #if BUILDFLAG(IS_ANDROID)
     display::Screen::SetScreenInstance(nullptr);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index f4fc156..9253f16c 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -131,7 +131,6 @@
 #if BUILDFLAG(IS_WIN)
 #include "ui/base/view_prop.h"
 #include "ui/base/win/window_event_target.h"
-#include "ui/display/win/test/scoped_screen_win.h"
 #endif
 
 using testing::_;
@@ -3766,11 +3765,6 @@
        ScrollEventsOverscrollWithFling) {
   SetUpOverscrollEnvironment();
 
-#if BUILDFLAG(IS_WIN)
-  // Create a ScopedScreenWin.
-  display::win::test::ScopedScreenWin scoped_screen_win;
-#endif
-
   // Send a wheel event. ACK the event as not processed. This should not
   // initiate an overscroll gesture since it doesn't cross the threshold yet.
   SimulateWheelEvent(10, 0, 0, true, WebMouseWheelEvent::kPhaseBegan);
@@ -3847,11 +3841,6 @@
        ScrollEventsOverscrollWithZeroFling) {
   SetUpOverscrollEnvironment();
 
-#if BUILDFLAG(IS_WIN)
-  // Create a ScopedScreenWin.
-  display::win::test::ScopedScreenWin scoped_screen_win;
-#endif
-
   // Send a wheel event. ACK the event as not processed. This should not
   // initiate an overscroll gesture since it doesn't cross the threshold yet.
   SimulateWheelEvent(10, 0, 0, true, WebMouseWheelEvent::kPhaseBegan);
@@ -3929,11 +3918,6 @@
        MAYBE_ReverseFlingCancelsOverscroll) {
   SetUpOverscrollEnvironment();
 
-#if BUILDFLAG(IS_WIN)
-  // Create a ScopedScreenWin.
-  display::win::test::ScopedScreenWin scoped_screen_win;
-#endif
-
   {
     PressAndSetTouchActionAuto();
     // Start and end a gesture in the same direction without processing the
@@ -4804,11 +4788,6 @@
        OverscrollStateResetsAfterScroll) {
   SetUpOverscrollEnvironment();
 
-#if BUILDFLAG(IS_WIN)
-  // Create a ScopedScreenWin.
-  display::win::test::ScopedScreenWin scoped_screen_win;
-#endif
-
   SimulateWheelEvent(0, 5, 0, true,
                      WebMouseWheelEvent::kPhaseBegan);  // sent directly
   SimulateWheelEvent(0, 30, 0, true,
@@ -5246,11 +5225,6 @@
 TEST_F(RenderWidgetHostViewAuraOverscrollTest, ScrollDeltasResetOnEnd) {
   SetUpOverscrollEnvironment();
 
-#if BUILDFLAG(IS_WIN)
-  // Create a ScopedScreenWin.
-  display::win::test::ScopedScreenWin scoped_screen_win;
-#endif
-
   PressAndSetTouchActionAuto();
   // Wheel event scroll ending with mouse move.
   SimulateWheelEvent(-30, -10, 0, true,
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
index c79d604..8f8fb19c 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm
@@ -27,6 +27,7 @@
 #include "testing/platform_test.h"
 #include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/layout.h"
+#include "ui/display/screen.h"
 
 using content::RenderWidgetHostViewMac;
 
@@ -122,6 +123,7 @@
   void TearDown() override { ImageTransportFactory::Terminate(); }
 
  private:
+  display::ScopedNativeScreen screen_;
   // This has a MessageLoop for ImageTransportFactory and enables
   // BrowserThread::UI for RecyclableCompositorMac used by
   // RenderWidgetHostViewMac.
diff --git a/content/browser/resources/gpu/BUILD.gn b/content/browser/resources/gpu/BUILD.gn
new file mode 100644
index 0000000..9a4a963
--- /dev/null
+++ b/content/browser/resources/gpu/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+  js_files = [
+    "info_view.js",
+    "info_view_table.js",
+    "info_view_table_row.js",
+  ]
+}
diff --git a/content/browser/resources/gpu/gpu_internals.html b/content/browser/resources/gpu/gpu_internals.html
index 0f13f54..092da842 100644
--- a/content/browser/resources/gpu/gpu_internals.html
+++ b/content/browser/resources/gpu/gpu_internals.html
@@ -8,35 +8,31 @@
 <head>
 <meta name="viewport" content="width=device-width" />
 <style>
-* {
-  box-sizing: border-box;
-  user-select: none;
-}
+  * {
+    box-sizing: border-box;
+    user-select: none;
+  }
 
-body {
-  background-color: white;
-  cursor: default;
-  font-family: sans-serif;
-  padding: 0;
-}
+  body {
+    background-color: white;
+    cursor: default;
+    font-family: sans-serif;
+    margin: 8px;
+    padding: 0;
+  }
 
-#debug-div {
-  border: 1px solid red;
-  display: -webkit-box;
-  left: 50%;
-  position: fixed;
-  top: 0;
-}
-
+  #debug-div {
+    border: 1px solid red;
+    display: flex;
+    left: 50%;
+    position: fixed;
+    top: 0;
+  }
 </style>
-<link rel="stylesheet" href="info_view.css">
-<link rel="stylesheet" href="chrome://resources/css/widgets.css">
 <script type="module" src="gpu_internals.js"></script>
 </head>
 <body>
-  <div id="debug-div">
-  </div>
-  <include src="info_view.html">
-  <script src="chrome://resources/js/jstemplate_compiled.js"></script>
+  <div id="debug-div"></div>
+  <info-view></info-view>
 </body>
 </html>
diff --git a/content/browser/resources/gpu/gpu_internals.js b/content/browser/resources/gpu/gpu_internals.js
index 0dc4da9..1c3f6d95 100644
--- a/content/browser/resources/gpu/gpu_internals.js
+++ b/content/browser/resources/gpu/gpu_internals.js
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {decorate} from 'chrome://resources/js/cr/ui.m.js';
+import './info_view.js';
 
 import {BrowserBridge} from './browser_bridge.js';
-import {makeInfoView} from './info_view.js';
 
 // Injected script from C++ or test environments may reference `browserBridge`
 // as a property of the global object.
@@ -16,7 +15,8 @@
  */
 function onLoad() {
   // Create the views.
-  decorate('#info-view', makeInfoView(window.browserBridge));
+  document.querySelector('info-view')
+      .addBrowserBridgeListeners(window.browserBridge);
 
   // Because of inherent raciness (between the deprecated DevTools API which
   // telemtry uses to drive the relevant tests, and the asynchronous loading of
diff --git a/content/browser/resources/gpu/info_view.css b/content/browser/resources/gpu/info_view.css
deleted file mode 100644
index 0709d98..0000000
--- a/content/browser/resources/gpu/info_view.css
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-#info-view {
-  -webkit-box-flex: 1;
-  overflow: auto;
-  padding: 10px;
-}
-
-#info-view * {
-  -webkit-user-select: text;
-}
-
-#info-view[selected] {
-  -webkit-box-orient: vertical;
-  display: -webkit-box;
-}
-
-#info-view h3,
-#info-view ul {
-  margin-bottom: 0;
-  margin-top: 0;
-}
-
-#info-view > div {
-  margin-bottom: 1em;
-}
-
-#info-view .row-title {
-  font-weight: bold;
-}
-
-#info-view table {
-  border-collapse: collapse;
-  cursor: auto;
-  table-layout: fixed;
-  width: 100%;
-}
-
-#info-view table,
-#info-view th,
-#info-view td {
-  border: 1px solid #777;
-  padding-end: 4px;
-  padding-start: 4px;
-  text-align: top;
-}
-
-#info-view td {
-  overflow-x: auto;
-}
-
-#info-view .feature-green {
-  color: rgb(0, 128, 0);
-}
-
-#info-view .feature-yellow {
-  color: rgb(128, 128, 0);
-}
-
-#info-view .feature-red {
-  color: rgb(255, 0, 0);
-}
-
-#info-view .feature-gray {
-  color: rgb(128, 128, 128);
-}
-
-#info-view .bg-yellow {
-  background-color: yellow;
-}
-
-#vulkan-info-value {
-  white-space: pre;
-}
-
-#copy-to-clipboard {
-  user-select: none;
-}
diff --git a/content/browser/resources/gpu/info_view.html b/content/browser/resources/gpu/info_view.html
index 0753fad..b9de047 100644
--- a/content/browser/resources/gpu/info_view.html
+++ b/content/browser/resources/gpu/info_view.html
@@ -3,139 +3,183 @@
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
-<div id="info-view">
-  <div>
-    <input type="button" id="copy-to-clipboard" value="Copy Report to Clipboard">
-  </div>
-  <div>
-    <h3>Graphics Feature Status</h3>
-    <ul class="feature-status-list">
-    </ul>
-  </div>
+<style>
+  :host {
+    display: block;
+    flex: 1;
+    overflow: auto;
+    padding: 10px;
+  }
 
-  <div class='workarounds-div'>
-    <h3>Driver Bug Workarounds</h3>
-    <ul class="workarounds-list">
-    </ul>
-  </div>
+  :host * {
+    user-select: text;
+  }
 
-  <div class='problems-div'>
-    <h3>Problems Detected</h3>
-    <ul class="problems-list">
-    </ul>
-  </div>
+  :host([selected]) {
+    display: flex;
+    flex-direction: column;
+  }
 
-  <div class='angle-features-div'>
-    <h3>ANGLE Features</h3>
-    <ul class="angle-features-list">
-    </ul>
-  </div>
+  h3,
+  ul {
+    margin-bottom: 0;
+    margin-top: 0;
+  }
 
-  <div class='dawn-info-div'>
-    <h3>DAWN Info</h3>
-    <ul class="dawn-info-list">
-    </ul>
-  </div>
+  :host > div {
+    margin-bottom: 1em;
+  }
 
-  <div>
-    <h3>Version Information</h3>
-    <div id="client-info"></div>
-  </div>
+  .feature-green {
+    color: rgb(0, 128, 0);
+  }
 
-  <div>
-    <h3>Driver Information</h3>
-    <div id="basic-info"></div>
-  </div>
+  .feature-yellow {
+    color: rgb(128, 128, 0);
+  }
 
-  <div>
-    <h3>Compositor Information</h3>
-    <div id="compositor-info"></div>
-  </div>
+  .feature-red {
+    color: rgb(255, 0, 0);
+  }
 
-  <div>
-    <h3>GpuMemoryBuffers Status</h3>
-    <div id="gpu-memory-buffer-info"></div>
-  </div>
+  .feature-gray {
+    color: rgb(128, 128, 128);
+  }
 
-  <div>
-    <h3>Display(s) Information</h3>
-    <div id="display-info"></div>
-  </div>
+  .bg-yellow {
+    background-color: yellow;
+  }
 
-  <div>
-    <h3>Video Acceleration Information</h3>
-    <div id="video-acceleration-info"></div>
-  </div>
+  #vulkan-info-value {
+    white-space: pre;
+  }
 
-  <div>
-    <h3>Vulkan Information</h3>
-    <div id="vulkan-info"></div>
-  </div>
+  #copy-to-clipboard {
+    background-image: linear-gradient(#ededed, #ededed 38%, #dedede);
+    border: 1px solid rgba(0, 0, 0, .25);
+    border-radius: 2px;
+    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08),
+        inset 0 1px 2px rgba(255, 255, 255, 0.75);
+    color: #444;
+    font: inherit;
+    margin: 0 1px 0 0;
+    min-height: 2em;
+    outline: none;
+    padding: 1px 10px;
+    text-shadow: 0 1px 0 rgb(240, 240, 240);
+    user-select: none;
+  }
 
-  <div>
-    <h3>Device Performance Information</h3>
-    <div id="device-perf-info"></div>
-  </div>
+  #copy-to-clipboard:enabled:hover {
+    background-image: linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
+    border-color: rgba(0, 0, 0, 0.3);
+    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12),
+        inset 0 1px 2px rgba(255, 255, 255, 0.95);
+    color: black;
+  }
 
-  <div class="diagnostics">
-    <h3>Diagnostics</h3>
-    <div class="diagnostics-loading">... loading ...</div>
-    <div id="diagnostics-table">None</div>
-  </div>
+  #copy-to-clipboard:enabled:active {
+    background-image: linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
+    box-shadow: none;
+    text-shadow: none;
+  }
+</style>
 
-  <div class='basic-info-for-hardware-gpu-div'>
-    <h3>Driver Information for Hardware GPU</h3>
-    <div id="basic-info-for-hardware-gpu"></div>
-  </div>
+<div>
+  <button id="copy-to-clipboard">Copy Report to Clipboard</button>
+</div>
+<div>
+  <h3>Graphics Feature Status</h3>
+  <ul class="feature-status-list"></ul>
+</div>
 
-  <div class='feature-status-for-hardware-gpu-div'>
-    <h3>Graphics Feature Status for Hardware GPU</h3>
-    <ul class="feature-status-for-hardware-gpu-list">
-    </ul>
-  </div>
+<div class='workarounds-div'>
+  <h3>Driver Bug Workarounds</h3>
+  <ul class="workarounds-list"></ul>
+</div>
 
-  <div class='workarounds-for-hardware-gpu-div'>
-    <h3>Driver Bug Workarounds for Hardware GPU</h3>
-    <ul class="workarounds-for-hardware-gpu-list">
-    </ul>
-  </div>
+<div class='problems-div'>
+  <h3>Problems Detected</h3>
+  <ul class="problems-list"></ul>
+</div>
 
-  <div class='problems-for-hardware-gpu-div'>
-    <h3>Problems Detected for Hardware GPU</h3>
-    <ul class="problems-for-hardware-gpu-list">
-    </ul>
-  </div>
+<div class='angle-features-div'>
+  <h3>ANGLE Features</h3>
+  <ul class="angle-features-list"></ul>
+</div>
 
-  <div id="log-messages" jsdisplay="values.length">
-    <h3>Log Messages</h3>
-    <ul>
-      <li jsselect="values">
-        <span jscontent="header"></span>: <span jscontent="message"></span>
-      </li>
-    </ul>
-  </div>
+<div class='dawn-info-div'>
+  <h3>DAWN Info</h3>
+  <ul class="dawn-info-list"></ul>
+</div>
 
-  <!-- templates -->
-  <div style="display:none">
-    <div id="info-view-table-template">
-      <table id="info-view-table">
-        <colgroup>
-          <col style="width: 25%" />
-          <col style="width: 75%" />
-        </colgroup>
-        <tr jsselect="value">
-          <td jsdisplay="!(value instanceof Array)">
-            <span class="row-title" jscontent="description">title</span>
-          </td>
-          <td jsdisplay="!(value instanceof Array)">
-            <span jscontent="value" jsvalues=".id:id">value</span>
-          </td>
-          <td jsdisplay="value instanceof Array" colspan=2>
-            <span jscontent="description" class="row-title"></span>
-            <div transclude="info-view-table-template"></div>
-          </td>
-        </tr>
-      </table>
-    </div>
-  </div>
+<div>
+  <h3>Version Information</h3>
+  <div id="client-info"></div>
+</div>
+
+<div>
+  <h3>Driver Information</h3>
+  <div id="basic-info"></div>
+</div>
+
+<div>
+  <h3>Compositor Information</h3>
+  <div id="compositor-info"></div>
+</div>
+
+<div>
+  <h3>GpuMemoryBuffers Status</h3>
+  <div id="gpu-memory-buffer-info"></div>
+</div>
+
+<div>
+  <h3>Display(s) Information</h3>
+  <div id="display-info"></div>
+</div>
+
+<div>
+  <h3>Video Acceleration Information</h3>
+  <div id="video-acceleration-info"></div>
+</div>
+
+<div>
+  <h3>Vulkan Information</h3>
+  <div id="vulkan-info"></div>
+</div>
+
+<div>
+  <h3>Device Performance Information</h3>
+  <div id="device-perf-info"></div>
+</div>
+
+<div class="diagnostics">
+  <h3>Diagnostics</h3>
+  <div class="diagnostics-loading">... loading ...</div>
+  <div id="diagnostics-table">None</div>
+</div>
+
+<div class='basic-info-for-hardware-gpu-div'>
+  <h3>Driver Information for Hardware GPU</h3>
+  <div id="basic-info-for-hardware-gpu"></div>
+</div>
+
+<div class='feature-status-for-hardware-gpu-div'>
+  <h3>Graphics Feature Status for Hardware GPU</h3>
+  <ul class="feature-status-for-hardware-gpu-list"></ul>
+</div>
+
+<div class='workarounds-for-hardware-gpu-div'>
+  <h3>Driver Bug Workarounds for Hardware GPU</h3>
+  <ul class="workarounds-for-hardware-gpu-list"></ul>
+</div>
+
+<div class='problems-for-hardware-gpu-div'>
+  <h3>Problems Detected for Hardware GPU</h3>
+  <ul class="problems-for-hardware-gpu-list"></ul>
+</div>
+
+<div id="log-messages">
+  <h3>Log Messages</h3>
+  <ul></ul>
 </div>
diff --git a/content/browser/resources/gpu/info_view.js b/content/browser/resources/gpu/info_view.js
index f85a768..bf70baa 100644
--- a/content/browser/resources/gpu/info_view.js
+++ b/content/browser/resources/gpu/info_view.js
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
-import {$} from 'chrome://resources/js/util.m.js';
+import './info_view_table.js';
+
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {VulkanInfo} from './vulkan_info.js';
 
@@ -12,613 +14,617 @@
  * hardware.  Its primary usefulness is to allow users to copy-paste
  * their data in an easy to read format for bug reports.
  */
-export function makeInfoView(browserBridge) {
+export class InfoViewElement extends CustomElement {
+  static get template() {
+    return getTrustedHTML`{__html_template__}`;
+  }
+
+  addBrowserBridgeListeners(browserBridge) {
+    browserBridge.addEventListener(
+        'gpuInfoUpdate', this.refresh.bind(this, browserBridge));
+    browserBridge.addEventListener(
+        'logMessagesChange', this.refresh.bind(this, browserBridge));
+    browserBridge.addEventListener(
+        'clientInfoChange', this.refresh.bind(this, browserBridge));
+    this.refresh(browserBridge);
+  }
+
+  connectedCallback() {
+    // Add handler to 'copy to clipboard' button
+    this.shadowRoot.querySelector('#copy-to-clipboard').onclick = (() => {
+      // Make sure nothing is selected
+      const s = window.getSelection();
+      s.removeAllRanges();
+      s.selectAllChildren(this.shadowRoot);
+      document.execCommand('copy');
+
+      // And deselect everything at the end.
+      window.getSelection().removeAllRanges();
+    });
+  }
+
   /**
-   * Provides information on the GPU process and underlying graphics hardware.
-   * @constructor
+   * Updates the view based on its currently known data
    */
-  const InfoView = crUiDefine('div');
-
-  InfoView.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    decorate: function() {
-      browserBridge.addEventListener('gpuInfoUpdate', this.refresh.bind(this));
-      browserBridge.addEventListener(
-          'logMessagesChange', this.refresh.bind(this));
-      browserBridge.addEventListener(
-          'clientInfoChange', this.refresh.bind(this));
-
-      // Add handler to 'copy to clipboard' button
-      $('copy-to-clipboard').onclick = function() {
-        // Make sure nothing is selected
-        window.getSelection().removeAllRanges();
-
-        document.execCommand('selectAll');
-        document.execCommand('copy');
-
-        // And deselect everything at the end.
-        window.getSelection().removeAllRanges();
-      };
-
-      this.refresh();
-    },
-
-    /**
-     * Updates the view based on its currently known data
-     */
-    refresh: function(data) {
-      let clientInfo;
-      function createSourcePermalink(revisionIdentifier, filepath) {
-        if (revisionIdentifier.length !== 40) {
-          // If the revision id isn't a hash, just use the 0.0.0.0 version
-          // from the Chrome version string "Chrome/0.0.0.0".
-          revisionIdentifier = clientInfo.version.split('/')[1];
-        }
-        return `https://chromium.googlesource.com/chromium/src/+/${
-            revisionIdentifier}/${filepath}`;
+  refresh(browserBridge) {
+    let clientInfo;
+    function createSourcePermalink(revisionIdentifier, filepath) {
+      if (revisionIdentifier.length !== 40) {
+        // If the revision id isn't a hash, just use the 0.0.0.0 version
+        // from the Chrome version string "Chrome/0.0.0.0".
+        revisionIdentifier = clientInfo.version.split('/')[1];
       }
+      return `https://chromium.googlesource.com/chromium/src/+/${
+          revisionIdentifier}/${filepath}`;
+    }
 
-      // Client info
-      if (browserBridge.clientInfo) {
-        clientInfo = browserBridge.clientInfo;
+    // Client info
+    if (browserBridge.clientInfo) {
+      clientInfo = browserBridge.clientInfo;
 
-        this.setTable_('client-info', [
-          {description: 'Data exported', value: (new Date()).toISOString()},
-          {description: 'Chrome version', value: clientInfo.version},
-          {description: 'Operating system', value: clientInfo.operating_system},
-          {
-            description: 'Software rendering list URL',
-            value: createSourcePermalink(
-                clientInfo.revision_identifier,
-                'gpu/config/software_rendering_list.json')
-          },
-          {
-            description: 'Driver bug list URL',
-            value: createSourcePermalink(
-                clientInfo.revision_identifier,
-                'gpu/config/gpu_driver_bug_list.json')
-          },
-          {description: 'ANGLE commit id', value: clientInfo.angle_commit_id}, {
-            description: '2D graphics backend',
-            value: clientInfo.graphics_backend
-          },
-          {description: 'Command Line', value: clientInfo.command_line}
-        ]);
+      this.setTable_('client-info', [
+        {description: 'Data exported', value: (new Date()).toISOString()},
+        {description: 'Chrome version', value: clientInfo.version},
+        {description: 'Operating system', value: clientInfo.operating_system}, {
+          description: 'Software rendering list URL',
+          value: createSourcePermalink(
+              clientInfo.revision_identifier,
+              'gpu/config/software_rendering_list.json')
+        },
+        {
+          description: 'Driver bug list URL',
+          value: createSourcePermalink(
+              clientInfo.revision_identifier,
+              'gpu/config/gpu_driver_bug_list.json')
+        },
+        {description: 'ANGLE commit id', value: clientInfo.angle_commit_id}, {
+          description: '2D graphics backend',
+          value: clientInfo.graphics_backend
+        },
+        {description: 'Command Line', value: clientInfo.command_line}
+      ]);
+    } else {
+      this.setText_('client-info', '... loading...');
+    }
+
+
+    // GPU info, basic
+    const diagnosticsDiv = this.shadowRoot.querySelector('.diagnostics');
+    const diagnosticsLoadingDiv =
+        this.shadowRoot.querySelector('.diagnostics-loading');
+    const featureStatusList =
+        this.shadowRoot.querySelector('.feature-status-list');
+    const problemsDiv = this.shadowRoot.querySelector('.problems-div');
+    const problemsList = this.shadowRoot.querySelector('.problems-list');
+    const workaroundsDiv = this.shadowRoot.querySelector('.workarounds-div');
+    const workaroundsList = this.shadowRoot.querySelector('.workarounds-list');
+    const ANGLEFeaturesDiv =
+        this.shadowRoot.querySelector('.angle-features-div');
+    const ANGLEFeaturesList =
+        this.shadowRoot.querySelector('.angle-features-list');
+    const DAWNInfoDiv = this.shadowRoot.querySelector('.dawn-info-div');
+    const DAWNInfoList = this.shadowRoot.querySelector('.dawn-info-list');
+
+    const basicInfoForHardwareGpuDiv =
+        this.shadowRoot.querySelector('.basic-info-for-hardware-gpu-div');
+    const featureStatusForHardwareGpuDiv =
+        this.shadowRoot.querySelector('.feature-status-for-hardware-gpu-div');
+    const featureStatusForHardwareGpuList =
+        this.shadowRoot.querySelector('.feature-status-for-hardware-gpu-list');
+    const problemsForHardwareGpuDiv =
+        this.shadowRoot.querySelector('.problems-for-hardware-gpu-div');
+    const problemsForHardwareGpuList =
+        this.shadowRoot.querySelector('.problems-for-hardware-gpu-list');
+    const workaroundsForHardwareGpuDiv =
+        this.shadowRoot.querySelector('.workarounds-for-hardware-gpu-div');
+    const workaroundsForHardwareGpuList =
+        this.shadowRoot.querySelector('.workarounds-for-hardware-gpu-list');
+
+    const gpuInfo = browserBridge.gpuInfo;
+    let i;
+    if (gpuInfo) {
+      // Not using jstemplate here for blocklist status because we construct
+      // href from data, which jstemplate can't seem to do.
+      if (gpuInfo.featureStatus) {
+        this.appendFeatureInfo_(
+            gpuInfo.featureStatus, featureStatusList, problemsDiv, problemsList,
+            workaroundsDiv, workaroundsList);
       } else {
-        this.setText_('client-info', '... loading...');
-      }
-
-
-      // GPU info, basic
-      const diagnosticsDiv = this.querySelector('.diagnostics');
-      const diagnosticsLoadingDiv = this.querySelector('.diagnostics-loading');
-      const featureStatusList = this.querySelector('.feature-status-list');
-      const problemsDiv = this.querySelector('.problems-div');
-      const problemsList = this.querySelector('.problems-list');
-      const workaroundsDiv = this.querySelector('.workarounds-div');
-      const workaroundsList = this.querySelector('.workarounds-list');
-      const ANGLEFeaturesDiv = this.querySelector('.angle-features-div');
-      const ANGLEFeaturesList = this.querySelector('.angle-features-list');
-      const DAWNInfoDiv = this.querySelector('.dawn-info-div');
-      const DAWNInfoList = this.querySelector('.dawn-info-list');
-
-      const basicInfoForHardwareGpuDiv =
-          this.querySelector('.basic-info-for-hardware-gpu-div');
-      const featureStatusForHardwareGpuDiv =
-          this.querySelector('.feature-status-for-hardware-gpu-div');
-      const featureStatusForHardwareGpuList =
-          this.querySelector('.feature-status-for-hardware-gpu-list');
-      const problemsForHardwareGpuDiv =
-          this.querySelector('.problems-for-hardware-gpu-div');
-      const problemsForHardwareGpuList =
-          this.querySelector('.problems-for-hardware-gpu-list');
-      const workaroundsForHardwareGpuDiv =
-          this.querySelector('.workarounds-for-hardware-gpu-div');
-      const workaroundsForHardwareGpuList =
-          this.querySelector('.workarounds-for-hardware-gpu-list');
-
-      const gpuInfo = browserBridge.gpuInfo;
-      let i;
-      if (gpuInfo) {
-        // Not using jstemplate here for blocklist status because we construct
-        // href from data, which jstemplate can't seem to do.
-        if (gpuInfo.featureStatus) {
-          this.appendFeatureInfo_(
-              gpuInfo.featureStatus, featureStatusList, problemsDiv,
-              problemsList, workaroundsDiv, workaroundsList);
-        } else {
-          featureStatusList.textContent = '';
-          problemsList.hidden = true;
-          workaroundsList.hidden = true;
-        }
-
-        if (gpuInfo.featureStatusForHardwareGpu) {
-          basicInfoForHardwareGpuDiv.hidden = false;
-          featureStatusForHardwareGpuDiv.hidden = false;
-          problemsForHardwareGpuDiv.hidden = false;
-          workaroundsForHardwareGpuDiv.hidden = false;
-          this.appendFeatureInfo_(
-              gpuInfo.featureStatusForHardwareGpu,
-              featureStatusForHardwareGpuList, problemsForHardwareGpuDiv,
-              problemsForHardwareGpuList, workaroundsForHardwareGpuDiv,
-              workaroundsForHardwareGpuList);
-          if (gpuInfo.basicInfoForHardwareGpu) {
-            this.setTable_(
-                'basic-info-for-hardware-gpu', gpuInfo.basicInfoForHardwareGpu);
-          } else {
-            this.setTable_('basic-info-for-hardware-gpu', []);
-          }
-        } else {
-          basicInfoForHardwareGpuDiv.hidden = true;
-          featureStatusForHardwareGpuDiv.hidden = true;
-          problemsForHardwareGpuDiv.hidden = true;
-          workaroundsForHardwareGpuDiv.hidden = true;
-        }
-
-        if (gpuInfo.basicInfo) {
-          this.setTable_('basic-info', gpuInfo.basicInfo);
-        } else {
-          this.setTable_('basic-info', []);
-        }
-
-        if (gpuInfo.compositorInfo) {
-          this.setTable_('compositor-info', gpuInfo.compositorInfo);
-        } else {
-          this.setTable_('compositor-info', []);
-        }
-
-        if (gpuInfo.gpuMemoryBufferInfo) {
-          this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo);
-        } else {
-          this.setTable_('gpu-memory-buffer-info', []);
-        }
-
-        if (gpuInfo.displayInfo) {
-          this.setTable_('display-info', gpuInfo.displayInfo);
-        } else {
-          this.setTable_('display-info', []);
-        }
-
-        if (gpuInfo.videoAcceleratorsInfo) {
-          this.setTable_(
-              'video-acceleration-info', gpuInfo.videoAcceleratorsInfo);
-        } else {
-          this.setTable_('video-acceleration-info', []);
-        }
-
-        if (gpuInfo.ANGLEFeatures) {
-          if (gpuInfo.ANGLEFeatures.length) {
-            ANGLEFeaturesDiv.hidden = false;
-            ANGLEFeaturesList.textContent = '';
-            for (const ANGLEFeature of gpuInfo.ANGLEFeatures) {
-              const ANGLEFeatureEl = this.createANGLEFeatureEl_(ANGLEFeature);
-              ANGLEFeaturesList.appendChild(ANGLEFeatureEl);
-            }
-          } else {
-            ANGLEFeaturesDiv.hidden = true;
-          }
-        }
-
-        if (gpuInfo.dawnInfo) {
-          if (gpuInfo.dawnInfo.length) {
-            DAWNInfoDiv.hidden = false;
-            this.createDawnInfoEl_(DAWNInfoList, gpuInfo.dawnInfo);
-          } else {
-            DAWNInfoDiv.hidden = true;
-          }
-        }
-
-        if (gpuInfo.diagnostics) {
-          diagnosticsDiv.hidden = false;
-          diagnosticsLoadingDiv.hidden = true;
-          $('diagnostics-table').hidden = false;
-          this.setTable_('diagnostics-table', gpuInfo.diagnostics);
-        } else if (gpuInfo.diagnostics === null) {
-          // gpu_internals.cc sets diagnostics to null when it is being loaded
-          diagnosticsDiv.hidden = false;
-          diagnosticsLoadingDiv.hidden = false;
-          $('diagnostics-table').hidden = true;
-        } else {
-          diagnosticsDiv.hidden = true;
-        }
-
-        if (gpuInfo.vulkanInfo) {
-          const vulkanInfo = new VulkanInfo(gpuInfo.vulkanInfo);
-          const data = [{
-            'description': 'info',
-            'value': vulkanInfo.toString(),
-            'id': 'vulkan-info-value'
-          }];
-          this.setTable_('vulkan-info', data);
-        } else {
-          this.setTable_('vulkan-info', []);
-        }
-
-        if (gpuInfo.devicePerfInfo) {
-          this.setTable_('device-perf-info', gpuInfo.devicePerfInfo);
-        } else {
-          this.setTable_('device-perf-info', []);
-        }
-      } else {
-        this.setText_('basic-info', '... loading ...');
-        diagnosticsDiv.hidden = true;
         featureStatusList.textContent = '';
-        problemsDiv.hidden = true;
-        DAWNInfoDiv.hidden = true;
+        problemsList.hidden = true;
+        workaroundsList.hidden = true;
       }
 
-      // Log messages
-      jstProcess(
-          new JsEvalContext({values: browserBridge.logMessages}),
-          $('log-messages'));
-    },
-
-    appendFeatureInfo_: function(
-        featureInfo, featureStatusList, problemsDiv, problemsList,
-        workaroundsDiv, workaroundsList) {
-      // Feature map
-      const featureLabelMap = {
-        '2d_canvas': 'Canvas',
-        'gpu_compositing': 'Compositing',
-        'webgl': 'WebGL',
-        'multisampling': 'WebGL multisampling',
-        'texture_sharing': 'Texture Sharing',
-        'video_decode': 'Video Decode',
-        'rasterization': 'Rasterization',
-        'opengl': 'OpenGL',
-        'metal': 'Metal',
-        'vulkan': 'Vulkan',
-        'multiple_raster_threads': 'Multiple Raster Threads',
-        'native_gpu_memory_buffers': 'Native GpuMemoryBuffers',
-        'protected_video_decode': 'Hardware Protected Video Decode',
-        'surface_control': 'Surface Control',
-        'vpx_decode': 'VPx Video Decode',
-        'webgl2': 'WebGL2',
-        'skia_renderer': 'Skia Renderer',
-        'canvas_oop_rasterization': 'Canvas out-of-process rasterization',
-        'raw_draw': 'Raw Draw',
-        'video_encode': 'Video Encode',
-        'direct_rendering_display_compositor':
-            'Direct Rendering Display Compositor',
-        'webgpu': 'WebGPU',
-      };
-
-      const statusMap = {
-        'disabled_software': {
-          'label': 'Software only. Hardware acceleration disabled',
-          'class': 'feature-yellow'
-        },
-        'disabled_off': {'label': 'Disabled', 'class': 'feature-red'},
-        'disabled_off_ok': {'label': 'Disabled', 'class': 'feature-yellow'},
-        'unavailable_software': {
-          'label': 'Software only, hardware acceleration unavailable',
-          'class': 'feature-yellow'
-        },
-        'unavailable_off': {'label': 'Unavailable', 'class': 'feature-red'},
-        'unavailable_off_ok':
-            {'label': 'Unavailable', 'class': 'feature-yellow'},
-        'enabled_readback': {
-          'label': 'Hardware accelerated but at reduced performance',
-          'class': 'feature-yellow'
-        },
-        'enabled_force': {
-          'label': 'Hardware accelerated on all pages',
-          'class': 'feature-green'
-        },
-        'enabled': {'label': 'Hardware accelerated', 'class': 'feature-green'},
-        'enabled_on': {'label': 'Enabled', 'class': 'feature-green'},
-        'enabled_force_on':
-            {'label': 'Force enabled', 'class': 'feature-green'},
-      };
-
-      // feature status list
-      featureStatusList.textContent = '';
-      for (const featureName in featureInfo.featureStatus) {
-        const featureStatus = featureInfo.featureStatus[featureName];
-        const featureEl = document.createElement('li');
-
-        const nameEl = document.createElement('span');
-        if (!featureLabelMap[featureName]) {
-          console.info('Missing featureLabel for', featureName);
-        }
-        nameEl.textContent = featureLabelMap[featureName] + ': ';
-        featureEl.appendChild(nameEl);
-
-        const statusEl = document.createElement('span');
-        const statusInfo = statusMap[featureStatus];
-        if (!statusInfo) {
-          console.info('Missing status for ', featureStatus);
-          statusEl.textContent = 'Unknown';
-          statusEl.className = 'feature-red';
+      if (gpuInfo.featureStatusForHardwareGpu) {
+        basicInfoForHardwareGpuDiv.hidden = false;
+        featureStatusForHardwareGpuDiv.hidden = false;
+        problemsForHardwareGpuDiv.hidden = false;
+        workaroundsForHardwareGpuDiv.hidden = false;
+        this.appendFeatureInfo_(
+            gpuInfo.featureStatusForHardwareGpu,
+            featureStatusForHardwareGpuList, problemsForHardwareGpuDiv,
+            problemsForHardwareGpuList, workaroundsForHardwareGpuDiv,
+            workaroundsForHardwareGpuList);
+        if (gpuInfo.basicInfoForHardwareGpu) {
+          this.setTable_(
+              'basic-info-for-hardware-gpu', gpuInfo.basicInfoForHardwareGpu);
         } else {
-          statusEl.textContent = statusInfo['label'];
-          statusEl.className = statusInfo['class'];
-        }
-        featureEl.appendChild(statusEl);
-
-        featureStatusList.appendChild(featureEl);
-      }
-
-      // problems list
-      if (featureInfo.problems.length) {
-        problemsDiv.hidden = false;
-        problemsList.textContent = '';
-        for (const problem of featureInfo.problems) {
-          const problemEl = this.createProblemEl_(problem);
-          problemsList.appendChild(problemEl);
+          this.setTable_('basic-info-for-hardware-gpu', []);
         }
       } else {
-        problemsDiv.hidden = true;
+        basicInfoForHardwareGpuDiv.hidden = true;
+        featureStatusForHardwareGpuDiv.hidden = true;
+        problemsForHardwareGpuDiv.hidden = true;
+        workaroundsForHardwareGpuDiv.hidden = true;
       }
 
-      // driver bug workarounds list
-      if (featureInfo.workarounds.length) {
-        workaroundsDiv.hidden = false;
-        workaroundsList.textContent = '';
-        for (const workaround of featureInfo.workarounds) {
-          const workaroundEl = document.createElement('li');
-          workaroundEl.textContent = workaround;
-          workaroundsList.appendChild(workaroundEl);
-        }
+      if (gpuInfo.basicInfo) {
+        this.setTable_('basic-info', gpuInfo.basicInfo);
       } else {
-        workaroundsDiv.hidden = true;
+        this.setTable_('basic-info', []);
       }
-    },
 
-    createProblemEl_: function(problem) {
-      const problemEl = document.createElement('li');
-
-      // Description of issue
-      const desc = document.createElement('a');
-      let text = problem.description;
-      const pattern = ' Please update your graphics driver via this link: ';
-      const pos = text.search(pattern);
-      let url = '';
-      if (pos > 0) {
-        url = text.substring(pos + pattern.length);
-        text = text.substring(0, pos);
+      if (gpuInfo.compositorInfo) {
+        this.setTable_('compositor-info', gpuInfo.compositorInfo);
+      } else {
+        this.setTable_('compositor-info', []);
       }
-      desc.textContent = text;
-      problemEl.appendChild(desc);
 
-      // Spacing ':' element
-      if (problem.crBugs.length > 0) {
+      if (gpuInfo.gpuMemoryBufferInfo) {
+        this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo);
+      } else {
+        this.setTable_('gpu-memory-buffer-info', []);
+      }
+
+      if (gpuInfo.displayInfo) {
+        this.setTable_('display-info', gpuInfo.displayInfo);
+      } else {
+        this.setTable_('display-info', []);
+      }
+
+      if (gpuInfo.videoAcceleratorsInfo) {
+        this.setTable_(
+            'video-acceleration-info', gpuInfo.videoAcceleratorsInfo);
+      } else {
+        this.setTable_('video-acceleration-info', []);
+      }
+
+      if (gpuInfo.ANGLEFeatures) {
+        if (gpuInfo.ANGLEFeatures.length) {
+          ANGLEFeaturesDiv.hidden = false;
+          ANGLEFeaturesList.textContent = '';
+          for (const ANGLEFeature of gpuInfo.ANGLEFeatures) {
+            const ANGLEFeatureEl = this.createANGLEFeatureEl_(ANGLEFeature);
+            ANGLEFeaturesList.appendChild(ANGLEFeatureEl);
+          }
+        } else {
+          ANGLEFeaturesDiv.hidden = true;
+        }
+      }
+
+      if (gpuInfo.dawnInfo) {
+        if (gpuInfo.dawnInfo.length) {
+          DAWNInfoDiv.hidden = false;
+          this.createDawnInfoEl_(DAWNInfoList, gpuInfo.dawnInfo);
+        } else {
+          DAWNInfoDiv.hidden = true;
+        }
+      }
+
+      if (gpuInfo.diagnostics) {
+        diagnosticsDiv.hidden = false;
+        diagnosticsLoadingDiv.hidden = true;
+        this.shadowRoot.querySelector('#diagnostics-table').hidden = false;
+        this.setTable_('diagnostics-table', gpuInfo.diagnostics);
+      } else if (gpuInfo.diagnostics === null) {
+        // gpu_internals.cc sets diagnostics to null when it is being loaded
+        diagnosticsDiv.hidden = false;
+        diagnosticsLoadingDiv.hidden = false;
+        this.shadowRoot.querySelector('#diagnostics-table').hidden = true;
+      } else {
+        diagnosticsDiv.hidden = true;
+      }
+
+      if (gpuInfo.vulkanInfo) {
+        const vulkanInfo = new VulkanInfo(gpuInfo.vulkanInfo);
+        const data = [{
+          'description': 'info',
+          'value': vulkanInfo.toString(),
+          'id': 'vulkan-info-value'
+        }];
+        this.setTable_('vulkan-info', data);
+      } else {
+        this.setTable_('vulkan-info', []);
+      }
+
+      if (gpuInfo.devicePerfInfo) {
+        this.setTable_('device-perf-info', gpuInfo.devicePerfInfo);
+      } else {
+        this.setTable_('device-perf-info', []);
+      }
+    } else {
+      this.setText_('basic-info', '... loading ...');
+      diagnosticsDiv.hidden = true;
+      featureStatusList.textContent = '';
+      problemsDiv.hidden = true;
+      DAWNInfoDiv.hidden = true;
+    }
+
+    // Log messages
+    const messageList = this.shadowRoot.querySelector('#log-messages > ul');
+    messageList.innerHTML =
+        window.trustedTypes ? window.trustedTypes.emptyHTML : '';
+    browserBridge.logMessages.forEach(messageObj => {
+      const messageEl = document.createElement('span');
+      messageEl.textContent = `${messageObj.header}: ${messageObj.message}`;
+      const li = document.createElement('li');
+      li.appendChild(messageEl);
+      messageList.appendChild(li);
+    });
+  }
+
+  appendFeatureInfo_(
+      featureInfo, featureStatusList, problemsDiv, problemsList, workaroundsDiv,
+      workaroundsList) {
+    // Feature map
+    const featureLabelMap = {
+      '2d_canvas': 'Canvas',
+      'gpu_compositing': 'Compositing',
+      'webgl': 'WebGL',
+      'multisampling': 'WebGL multisampling',
+      'texture_sharing': 'Texture Sharing',
+      'video_decode': 'Video Decode',
+      'rasterization': 'Rasterization',
+      'opengl': 'OpenGL',
+      'metal': 'Metal',
+      'vulkan': 'Vulkan',
+      'multiple_raster_threads': 'Multiple Raster Threads',
+      'native_gpu_memory_buffers': 'Native GpuMemoryBuffers',
+      'protected_video_decode': 'Hardware Protected Video Decode',
+      'surface_control': 'Surface Control',
+      'vpx_decode': 'VPx Video Decode',
+      'webgl2': 'WebGL2',
+      'skia_renderer': 'Skia Renderer',
+      'canvas_oop_rasterization': 'Canvas out-of-process rasterization',
+      'raw_draw': 'Raw Draw',
+      'video_encode': 'Video Encode',
+      'direct_rendering_display_compositor':
+          'Direct Rendering Display Compositor',
+      'webgpu': 'WebGPU',
+    };
+
+    const statusMap = {
+      'disabled_software': {
+        'label': 'Software only. Hardware acceleration disabled',
+        'class': 'feature-yellow'
+      },
+      'disabled_off': {'label': 'Disabled', 'class': 'feature-red'},
+      'disabled_off_ok': {'label': 'Disabled', 'class': 'feature-yellow'},
+      'unavailable_software': {
+        'label': 'Software only, hardware acceleration unavailable',
+        'class': 'feature-yellow'
+      },
+      'unavailable_off': {'label': 'Unavailable', 'class': 'feature-red'},
+      'unavailable_off_ok': {'label': 'Unavailable', 'class': 'feature-yellow'},
+      'enabled_readback': {
+        'label': 'Hardware accelerated but at reduced performance',
+        'class': 'feature-yellow'
+      },
+      'enabled_force': {
+        'label': 'Hardware accelerated on all pages',
+        'class': 'feature-green'
+      },
+      'enabled': {'label': 'Hardware accelerated', 'class': 'feature-green'},
+      'enabled_on': {'label': 'Enabled', 'class': 'feature-green'},
+      'enabled_force_on': {'label': 'Force enabled', 'class': 'feature-green'},
+    };
+
+    // feature status list
+    featureStatusList.textContent = '';
+    for (const featureName in featureInfo.featureStatus) {
+      const featureStatus = featureInfo.featureStatus[featureName];
+      const featureEl = document.createElement('li');
+
+      const nameEl = document.createElement('span');
+      if (!featureLabelMap[featureName]) {
+        console.info('Missing featureLabel for', featureName);
+      }
+      nameEl.textContent = featureLabelMap[featureName] + ': ';
+      featureEl.appendChild(nameEl);
+
+      const statusEl = document.createElement('span');
+      const statusInfo = statusMap[featureStatus];
+      if (!statusInfo) {
+        console.info('Missing status for ', featureStatus);
+        statusEl.textContent = 'Unknown';
+        statusEl.className = 'feature-red';
+      } else {
+        statusEl.textContent = statusInfo['label'];
+        statusEl.className = statusInfo['class'];
+      }
+      featureEl.appendChild(statusEl);
+
+      featureStatusList.appendChild(featureEl);
+    }
+
+    // problems list
+    if (featureInfo.problems.length) {
+      problemsDiv.hidden = false;
+      problemsList.textContent = '';
+      for (const problem of featureInfo.problems) {
+        const problemEl = this.createProblemEl_(problem);
+        problemsList.appendChild(problemEl);
+      }
+    } else {
+      problemsDiv.hidden = true;
+    }
+
+    // driver bug workarounds list
+    if (featureInfo.workarounds.length) {
+      workaroundsDiv.hidden = false;
+      workaroundsList.textContent = '';
+      for (const workaround of featureInfo.workarounds) {
+        const workaroundEl = document.createElement('li');
+        workaroundEl.textContent = workaround;
+        workaroundsList.appendChild(workaroundEl);
+      }
+    } else {
+      workaroundsDiv.hidden = true;
+    }
+  }
+
+  createProblemEl_(problem) {
+    const problemEl = document.createElement('li');
+
+    // Description of issue
+    const desc = document.createElement('a');
+    let text = problem.description;
+    const pattern = ' Please update your graphics driver via this link: ';
+    const pos = text.search(pattern);
+    let url = '';
+    if (pos > 0) {
+      url = text.substring(pos + pattern.length);
+      text = text.substring(0, pos);
+    }
+    desc.textContent = text;
+    problemEl.appendChild(desc);
+
+    // Spacing ':' element
+    if (problem.crBugs.length > 0) {
+      const tmp = document.createElement('span');
+      tmp.textContent = ': ';
+      problemEl.appendChild(tmp);
+    }
+
+    let nbugs = 0;
+    let j;
+
+    // crBugs
+    for (j = 0; j < problem.crBugs.length; ++j) {
+      if (nbugs > 0) {
         const tmp = document.createElement('span');
-        tmp.textContent = ': ';
+        tmp.textContent = ', ';
         problemEl.appendChild(tmp);
       }
 
-      let nbugs = 0;
-      let j;
+      const link = document.createElement('a');
+      const bugid = parseInt(problem.crBugs[j]);
+      link.textContent = bugid;
+      link.href = 'http://crbug.com/' + bugid;
+      problemEl.appendChild(link);
+      nbugs++;
+    }
 
-      // crBugs
-      for (j = 0; j < problem.crBugs.length; ++j) {
-        if (nbugs > 0) {
-          const tmp = document.createElement('span');
-          tmp.textContent = ', ';
-          problemEl.appendChild(tmp);
-        }
+    if (problem.affectedGpuSettings.length > 0) {
+      const brNode = document.createElement('br');
+      problemEl.appendChild(brNode);
 
-        const link = document.createElement('a');
-        const bugid = parseInt(problem.crBugs[j]);
-        link.textContent = bugid;
-        link.href = 'http://crbug.com/' + bugid;
-        problemEl.appendChild(link);
-        nbugs++;
+      const iNode = document.createElement('i');
+      problemEl.appendChild(iNode);
+
+      const headNode = document.createElement('span');
+      if (problem.tag === 'disabledFeatures') {
+        headNode.textContent = 'Disabled Features: ';
+      } else {  // problem.tag === 'workarounds'
+        headNode.textContent = 'Applied Workarounds: ';
       }
-
-      if (problem.affectedGpuSettings.length > 0) {
-        const brNode = document.createElement('br');
-        problemEl.appendChild(brNode);
-
-        const iNode = document.createElement('i');
-        problemEl.appendChild(iNode);
-
-        const headNode = document.createElement('span');
+      iNode.appendChild(headNode);
+      for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
+        if (j > 0) {
+          const separateNode = document.createElement('span');
+          separateNode.textContent = ', ';
+          iNode.appendChild(separateNode);
+        }
+        const nameNode = document.createElement('span');
         if (problem.tag === 'disabledFeatures') {
-          headNode.textContent = 'Disabled Features: ';
+          nameNode.classList.add('feature-red');
         } else {  // problem.tag === 'workarounds'
-          headNode.textContent = 'Applied Workarounds: ';
+          nameNode.classList.add('feature-yellow');
         }
-        iNode.appendChild(headNode);
-        for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
-          if (j > 0) {
-            const separateNode = document.createElement('span');
-            separateNode.textContent = ', ';
-            iNode.appendChild(separateNode);
-          }
-          const nameNode = document.createElement('span');
-          if (problem.tag === 'disabledFeatures') {
-            nameNode.classList.add('feature-red');
-          } else {  // problem.tag === 'workarounds'
-            nameNode.classList.add('feature-yellow');
-          }
-          nameNode.textContent = problem.affectedGpuSettings[j];
-          iNode.appendChild(nameNode);
-        }
+        nameNode.textContent = problem.affectedGpuSettings[j];
+        iNode.appendChild(nameNode);
       }
+    }
 
-      // Append driver update link.
-      if (pos > 0) {
-        const brNode = document.createElement('br');
-        problemEl.appendChild(brNode);
+    // Append driver update link.
+    if (pos > 0) {
+      const brNode = document.createElement('br');
+      problemEl.appendChild(brNode);
 
-        const bNode = document.createElement('b');
-        bNode.classList.add('bg-yellow');
-        problemEl.appendChild(bNode);
+      const bNode = document.createElement('b');
+      bNode.classList.add('bg-yellow');
+      problemEl.appendChild(bNode);
 
-        const tmp = document.createElement('span');
-        tmp.textContent = 'Please update your graphics driver via ';
-        bNode.appendChild(tmp);
+      const tmp = document.createElement('span');
+      tmp.textContent = 'Please update your graphics driver via ';
+      bNode.appendChild(tmp);
 
-        const link = document.createElement('a');
-        link.textContent = 'this link';
-        link.href = url;
-        bNode.appendChild(link);
-      }
+      const link = document.createElement('a');
+      link.textContent = 'this link';
+      link.href = url;
+      bNode.appendChild(link);
+    }
 
-      return problemEl;
-    },
+    return problemEl;
+  }
 
-    createANGLEFeatureEl_: function(ANGLEFeature) {
-      const ANGLEFeatureEl = document.createElement('li');
+  createANGLEFeatureEl_(ANGLEFeature) {
+    const ANGLEFeatureEl = document.createElement('li');
 
-      // Name comes first, bolded
-      const name = document.createElement('b');
-      name.textContent = ANGLEFeature.name;
-      ANGLEFeatureEl.appendChild(name);
+    // Name comes first, bolded
+    const name = document.createElement('b');
+    name.textContent = ANGLEFeature.name;
+    ANGLEFeatureEl.appendChild(name);
 
-      // If there's a category, it follows the name in parentheses
-      if (ANGLEFeature.category) {
-        const separator = document.createElement('span');
-        separator.textContent = ' ';
-        ANGLEFeatureEl.appendChild(separator);
-
-        const category = document.createElement('span');
-        category.textContent = '(' + ANGLEFeature.category + ')';
-        ANGLEFeatureEl.appendChild(category);
-      }
-
-      // If there's a bug link, try to parse the crbug/anglebug number
-      if (ANGLEFeature.bug) {
-        const separator = document.createElement('span');
-        separator.textContent = ' ';
-        ANGLEFeatureEl.appendChild(separator);
-
-        const bug = document.createElement('a');
-        if (ANGLEFeature.bug.includes('crbug.com/')) {
-          bug.textContent = ANGLEFeature.bug.match(/\d+/);
-        } else if (ANGLEFeature.bug.includes('anglebug.com/')) {
-          bug.textContent = 'anglebug:' + ANGLEFeature.bug.match(/\d+/);
-        } else {
-          bug.textContent = ANGLEFeature.bug;
-        }
-        bug.href = ANGLEFeature.bug;
-        ANGLEFeatureEl.appendChild(bug);
-      }
-
-      // Follow with a colon, and the status (colored)
+    // If there's a category, it follows the name in parentheses
+    if (ANGLEFeature.category) {
       const separator = document.createElement('span');
-      separator.textContent = ': ';
+      separator.textContent = ' ';
       ANGLEFeatureEl.appendChild(separator);
 
-      const status = document.createElement('span');
-      if (ANGLEFeature.status === 'enabled') {
-        status.textContent = 'Enabled';
-        status.classList.add('feature-green');
+      const category = document.createElement('span');
+      category.textContent = '(' + ANGLEFeature.category + ')';
+      ANGLEFeatureEl.appendChild(category);
+    }
+
+    // If there's a bug link, try to parse the crbug/anglebug number
+    if (ANGLEFeature.bug) {
+      const separator = document.createElement('span');
+      separator.textContent = ' ';
+      ANGLEFeatureEl.appendChild(separator);
+
+      const bug = document.createElement('a');
+      if (ANGLEFeature.bug.includes('crbug.com/')) {
+        bug.textContent = ANGLEFeature.bug.match(/\d+/);
+      } else if (ANGLEFeature.bug.includes('anglebug.com/')) {
+        bug.textContent = 'anglebug:' + ANGLEFeature.bug.match(/\d+/);
       } else {
-        status.textContent = 'Disabled';
-        status.classList.add('feature-red');
+        bug.textContent = ANGLEFeature.bug;
       }
-      ANGLEFeatureEl.appendChild(status);
+      bug.href = ANGLEFeature.bug;
+      ANGLEFeatureEl.appendChild(bug);
+    }
 
-      if (ANGLEFeature.condition) {
-        const condition = document.createElement('span');
-        condition.textContent = ': ' + ANGLEFeature.condition;
-        condition.classList.add('feature-gray');
-        ANGLEFeatureEl.appendChild(condition);
-      }
+    // Follow with a colon, and the status (colored)
+    const separator = document.createElement('span');
+    separator.textContent = ': ';
+    ANGLEFeatureEl.appendChild(separator);
 
-      // if there's a description, put on new line, italicized
-      if (ANGLEFeature.description) {
-        const brNode = document.createElement('br');
-        ANGLEFeatureEl.appendChild(brNode);
+    const status = document.createElement('span');
+    if (ANGLEFeature.status === 'enabled') {
+      status.textContent = 'Enabled';
+      status.classList.add('feature-green');
+    } else {
+      status.textContent = 'Disabled';
+      status.classList.add('feature-red');
+    }
+    ANGLEFeatureEl.appendChild(status);
 
-        const iNode = document.createElement('i');
-        ANGLEFeatureEl.appendChild(iNode);
+    if (ANGLEFeature.condition) {
+      const condition = document.createElement('span');
+      condition.textContent = ': ' + ANGLEFeature.condition;
+      condition.classList.add('feature-gray');
+      ANGLEFeatureEl.appendChild(condition);
+    }
 
-        const description = document.createElement('span');
-        description.textContent = ANGLEFeature.description;
-        iNode.appendChild(description);
-      }
+    // if there's a description, put on new line, italicized
+    if (ANGLEFeature.description) {
+      const brNode = document.createElement('br');
+      ANGLEFeatureEl.appendChild(brNode);
 
-      return ANGLEFeatureEl;
-    },
+      const iNode = document.createElement('i');
+      ANGLEFeatureEl.appendChild(iNode);
 
-    setText_: function(outputElementId, text) {
-      const peg = $(outputElementId);
-      peg.textContent = text;
-    },
+      const description = document.createElement('span');
+      description.textContent = ANGLEFeature.description;
+      iNode.appendChild(description);
+    }
 
-    setTable_: function(outputElementId, inputData) {
-      const template = jstGetTemplate('info-view-table-template');
-      jstProcess(new JsEvalContext({value: inputData}), template);
+    return ANGLEFeatureEl;
+  }
 
-      const peg = $(outputElementId);
-      if (!peg) {
-        throw new Error('Node ' + outputElementId + ' not found');
-      }
+  setText_(outputElementId, text) {
+    const peg = this.shadowRoot.querySelector(`#${outputElementId}`);
+    peg.textContent = text;
+  }
 
-      peg.innerHTML = trustedTypes.emptyHTML;
-      peg.appendChild(template);
-    },
+  setTable_(outputElementId, inputData) {
+    const table = document.createElement('info-view-table');
+    table.setData(inputData);
 
-    createDawnInfoEl_: function(DAWNInfoList, gpuDawnInfo) {
-      DAWNInfoList.textContent = '';
-      let inProcessingToggles = false;
+    const peg = this.shadowRoot.querySelector(`#${outputElementId}`);
+    if (!peg) {
+      throw new Error('Node ' + outputElementId + ' not found');
+    }
 
-      for (let i = 0; i < gpuDawnInfo.length; ++i) {
-        let infoString = gpuDawnInfo[i];
-        let infoEl;
+    peg.innerHTML = window.trustedTypes.emptyHTML;
+    peg.appendChild(table);
+  }
 
-        if (infoString.startsWith('<')) {
-          // GPU type and backend type.
-          // Add an empty line for the next adaptor.
-          const separator = document.createElement('br');
-          separator.textContent = '';
-          DAWNInfoList.appendChild(separator);
+  createDawnInfoEl_(DAWNInfoList, gpuDawnInfo) {
+    DAWNInfoList.textContent = '';
+    let inProcessingToggles = false;
 
-          // e.g. <Discrete GPU> D3D12 backend
-          infoEl = document.createElement('b');
-          infoEl.textContent = infoString;
-          DAWNInfoList.appendChild(infoEl);
-          // Go to the next line.
-          infoEl = document.createElement('br');
-          infoEl.textContent = '';
-          inProcessingToggles = false;
-        } else if (infoString.startsWith('[')) {
-          // e.g. [Default Toggle Names]
-          infoEl = document.createElement('span');
-          infoEl.classList.add('feature-green');
-          infoEl.textContent = infoString;
+    for (let i = 0; i < gpuDawnInfo.length; ++i) {
+      let infoString = gpuDawnInfo[i];
+      let infoEl;
 
-          if (infoString === '[Supported Features]') {
-            inProcessingToggles = false;
-          } else {
-            inProcessingToggles = true;
-          }
-        } else if (inProcessingToggles) {
-          // Each toggle takes 3 strings
-          infoEl = document.createElement('li');
+      if (infoString.startsWith('<')) {
+        // GPU type and backend type.
+        // Add an empty line for the next adaptor.
+        const separator = document.createElement('br');
+        separator.textContent = '';
+        DAWNInfoList.appendChild(separator);
 
-          // The toggle name comes first, bolded.
-          const name = document.createElement('b');
-          name.textContent = infoString + ':  ';
-          infoEl.appendChild(name);
-
-          // URL
-          infoString = gpuDawnInfo[++i];
-          const url = document.createElement('a');
-          url.textContent = infoString;
-          url.href = infoString;
-          infoEl.appendChild(url);
-
-          // Description, italicized
-          infoString = gpuDawnInfo[++i];
-          const description = document.createElement('i');
-          description.textContent = ':  ' + infoString;
-          infoEl.appendChild(description);
-        } else {
-          // Display supported extensions
-          infoEl = document.createElement('li');
-          infoEl.textContent = infoString;
-        }
-
+        // e.g. <Discrete GPU> D3D12 backend
+        infoEl = document.createElement('b');
+        infoEl.textContent = infoString;
         DAWNInfoList.appendChild(infoEl);
-      }
-    },
-  };
+        // Go to the next line.
+        infoEl = document.createElement('br');
+        infoEl.textContent = '';
+        inProcessingToggles = false;
+      } else if (infoString.startsWith('[')) {
+        // e.g. [Default Toggle Names]
+        infoEl = document.createElement('span');
+        infoEl.classList.add('feature-green');
+        infoEl.textContent = infoString;
 
-  return InfoView;
+        if (infoString === '[Supported Features]') {
+          inProcessingToggles = false;
+        } else {
+          inProcessingToggles = true;
+        }
+      } else if (inProcessingToggles) {
+        // Each toggle takes 3 strings
+        infoEl = document.createElement('li');
+
+        // The toggle name comes first, bolded.
+        const name = document.createElement('b');
+        name.textContent = infoString + ':  ';
+        infoEl.appendChild(name);
+
+        // URL
+        infoString = gpuDawnInfo[++i];
+        const url = document.createElement('a');
+        url.textContent = infoString;
+        url.href = infoString;
+        infoEl.appendChild(url);
+
+        // Description, italicized
+        infoString = gpuDawnInfo[++i];
+        const description = document.createElement('i');
+        description.textContent = ':  ' + infoString;
+        infoEl.appendChild(description);
+      } else {
+        // Display supported extensions
+        infoEl = document.createElement('li');
+        infoEl.textContent = infoString;
+      }
+
+      DAWNInfoList.appendChild(infoEl);
+    }
+  }
 }
+
+customElements.define('info-view', InfoViewElement);
diff --git a/content/browser/resources/gpu/info_view_table.html b/content/browser/resources/gpu/info_view_table.html
new file mode 100644
index 0000000..a29cbd35
--- /dev/null
+++ b/content/browser/resources/gpu/info_view_table.html
@@ -0,0 +1,12 @@
+<style>
+  :host {
+    display: flex;
+    cursor: auto;
+    width: 100%;
+  }
+
+  div {
+    width: 100%;
+  }
+</style>
+<div id="info-view-table"></div>
diff --git a/content/browser/resources/gpu/info_view_table.js b/content/browser/resources/gpu/info_view_table.js
new file mode 100644
index 0000000..07e5f69
--- /dev/null
+++ b/content/browser/resources/gpu/info_view_table.js
@@ -0,0 +1,24 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './info_view_table_row.js';
+
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
+
+export class InfoViewTableElement extends CustomElement {
+  static get template() {
+    return getTrustedHTML`{__html_template__}`;
+  }
+
+  setData(dataArray) {
+    dataArray.forEach(data => {
+      const row = document.createElement('info-view-table-row');
+      row.setData(data);
+      this.shadowRoot.querySelector('#info-view-table').appendChild(row);
+    });
+  }
+}
+
+customElements.define('info-view-table', InfoViewTableElement);
diff --git a/content/browser/resources/gpu/info_view_table_row.html b/content/browser/resources/gpu/info_view_table_row.html
new file mode 100644
index 0000000..4fd6046
--- /dev/null
+++ b/content/browser/resources/gpu/info_view_table_row.html
@@ -0,0 +1,48 @@
+<style>
+  :host {
+    border: none;
+    display: flex;
+  }
+
+  #array {
+    width: 100%;
+  }
+
+  #title {
+    overflow-x: auto;
+    width: 25%;
+  }
+
+  #value {
+    overflow-x: auto;
+    width: 75%;
+  }
+
+  :host(:not([is-array])) #array {
+    display: none;
+  }
+
+  :host([is-array]) #title,
+  :host([is-array]) #value {
+    display: none;
+  }
+
+  div {
+    border: 1px solid #777;
+    margin-inline-end: -1px;
+    margin-top: -1px;
+  }
+
+  .row-title {
+    font-weight: bold;
+  }
+</style>
+<div id="title">
+  <span class="row-title">title</span>
+</div>
+<div id="value">
+  <span>value</span>
+</div>
+<div id="array">
+  <span class="row-title"></span>
+</div>
diff --git a/content/browser/resources/gpu/info_view_table_row.js b/content/browser/resources/gpu/info_view_table_row.js
new file mode 100644
index 0000000..e569662d
--- /dev/null
+++ b/content/browser/resources/gpu/info_view_table_row.js
@@ -0,0 +1,39 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
+
+export class InfoViewTableRowElement extends CustomElement {
+  static get template() {
+    return getTrustedHTML`{__html_template__}`;
+  }
+
+  constructor() {
+    super();
+
+    this.data = null;
+  }
+
+  setData(data) {
+    const isArray = data.value instanceof Array;
+    this.toggleAttribute('is-array', isArray);
+    if (!isArray) {
+      this.shadowRoot.querySelector('.row-title').textContent =
+          data.description;
+      this.shadowRoot.querySelector('#value > span').textContent = data.value;
+      this.shadowRoot.querySelector('#value > span').id = data.id;
+    } else {
+      const array = this.shadowRoot.querySelector('#array');
+      array.querySelector('span').textContent = data.description;
+      data.value.forEach(value => {
+        const row = document.createElement('info-view-table-row');
+        row.setData(value);
+        array.appendChild(row);
+      });
+    }
+  }
+}
+
+customElements.define('info-view-table-row', InfoViewTableRowElement);
diff --git a/content/browser/screen_enumeration/screen_enumeration_browsertest.cc b/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
index 6267f67..feb76c9 100644
--- a/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
+++ b/content/browser/screen_enumeration/screen_enumeration_browsertest.cc
@@ -135,18 +135,26 @@
 
  protected:
   // ScreenEnumerationTest:
-  void SetUpOnMainThread() override {
-    ScreenEnumerationTest::SetUpOnMainThread();
-    original_screen_ = display::Screen::GetScreen();
-    display::Screen::SetScreenInstance(&screen_);
 
+  void SetUp() override {
+    display::Screen::SetScreenInstance(&screen_);
     // Create a shell that observes the fake screen. A display is required.
     screen()->display_list().AddDisplay({0, gfx::Rect(100, 100, 801, 802)},
                                         display::DisplayList::Type::PRIMARY);
+
+    ScreenEnumerationTest::SetUp();
+  }
+  void TearDown() override {
+    ScreenEnumerationTest::TearDown();
+    display::Screen::SetScreenInstance(nullptr);
+  }
+
+  void SetUpOnMainThread() override {
+    ScreenEnumerationTest::SetUpOnMainThread();
+
     test_shell_ = CreateBrowser();
   }
   void TearDownOnMainThread() override {
-    display::Screen::SetScreenInstance(original_screen_);
     ScreenEnumerationTest::TearDownOnMainThread();
   }
 
@@ -154,7 +162,6 @@
   Shell* test_shell() { return test_shell_; }
 
  private:
-  raw_ptr<display::Screen> original_screen_ = nullptr;
   display::ScreenBase screen_;
   raw_ptr<Shell> test_shell_ = nullptr;
 };
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 025bef9..127ad11 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -558,8 +558,10 @@
 
   // The host must be alive as long as |params->provider_info| is alive.
   owner_version_->worker_host()->CompleteStartWorkerPreparation(
-      process_id(), params->provider_info->browser_interface_broker
-                        .InitWithNewPipeAndPassReceiver());
+      process_id(),
+      params->provider_info->browser_interface_broker
+          .InitWithNewPipeAndPassReceiver(),
+      params->interface_provider.InitWithNewPipeAndPassRemote());
 
   // TODO(bashi): Always pass a valid outside fetch client settings object.
   // See crbug.com/937177.
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 9aba6de..8bc9112 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -843,6 +843,23 @@
   return running_service_workers_;
 }
 
+service_manager::InterfaceProvider*
+ServiceWorkerContextWrapper::GetRemoteInterfaces(
+    int64_t service_worker_version_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!context())
+    return nullptr;
+
+  auto* version = context()->GetLiveVersion(service_worker_version_id);
+  if (!version)
+    return nullptr;
+
+  CHECK(version->running_status() == EmbeddedWorkerStatus::STARTING ||
+        version->running_status() == EmbeddedWorkerStatus::RUNNING);
+
+  return &version->worker_host()->remote_interfaces();
+}
+
 scoped_refptr<ServiceWorkerRegistration>
 ServiceWorkerContextWrapper::GetLiveRegistration(int64_t registration_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index b88e2809..6bb75b6d 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -208,6 +208,8 @@
   void StopAllServiceWorkers(base::OnceClosure callback) override;
   const base::flat_map<int64_t, ServiceWorkerRunningInfo>&
   GetRunningServiceWorkerInfos() override;
+  service_manager::InterfaceProvider* GetRemoteInterfaces(
+      int64_t service_worker_version_id) override;
 
   scoped_refptr<ServiceWorkerRegistration> GetLiveRegistration(
       int64_t registration_id);
diff --git a/content/browser/service_worker/service_worker_host.cc b/content/browser/service_worker/service_worker_host.cc
index 8efd4a2..33af82d 100644
--- a/content/browser/service_worker/service_worker_host.cc
+++ b/content/browser/service_worker/service_worker_host.cc
@@ -65,13 +65,15 @@
 
 void ServiceWorkerHost::CompleteStartWorkerPreparation(
     int process_id,
-    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
-        broker_receiver) {
+    mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> broker_receiver,
+    mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
+        interface_provider_remote) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, worker_process_id_);
   DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id);
   worker_process_id_ = process_id;
   broker_receiver_.Bind(std::move(broker_receiver));
+  remote_interfaces_.Bind(std::move(interface_provider_remote));
 }
 
 void ServiceWorkerHost::CreateWebTransportConnector(
diff --git a/content/browser/service_worker/service_worker_host.h b/content/browser/service_worker/service_worker_host.h
index 7e3d08f..f1658ed 100644
--- a/content/browser/service_worker/service_worker_host.h
+++ b/content/browser/service_worker/service_worker_host.h
@@ -27,6 +27,7 @@
 #include "net/base/network_isolation_key.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
@@ -63,12 +64,18 @@
   int worker_process_id() const { return worker_process_id_; }
   ServiceWorkerVersion* version() const { return version_; }
 
+  service_manager::InterfaceProvider& remote_interfaces() {
+    return remote_interfaces_;
+  }
+
   // Completes initialization of this provider host. It is called once a
   // renderer process has been found to host the worker.
   void CompleteStartWorkerPreparation(
       int process_id,
       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
-          broker_receiver);
+          broker_receiver,
+      mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
+          interface_provider_remote);
 
   void CreateWebTransportConnector(
       mojo::PendingReceiver<blink::mojom::WebTransportConnector> receiver);
@@ -117,6 +124,9 @@
 
   std::unique_ptr<ServiceWorkerContainerHost> container_host_;
 
+  service_manager::InterfaceProvider remote_interfaces_{
+      base::ThreadTaskRunnerHandle::Get()};
+
   // CodeCacheHost processes requests to fetch / write generated code for
   // JavaScript / WebAssembly resources.
   std::unique_ptr<CodeCacheHostImpl::ReceiverSet> code_cache_host_receivers_;
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index 272c9200..86ccb48 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -365,9 +365,13 @@
       provider_info->host_remote.InitWithNewEndpointAndPassReceiver(),
       hosted_version, std::move(context));
 
+  mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
+      pending_interface_provider;
+
   host->CompleteStartWorkerPreparation(
       process_id,
-      provider_info->browser_interface_broker.InitWithNewPipeAndPassReceiver());
+      provider_info->browser_interface_broker.InitWithNewPipeAndPassReceiver(),
+      pending_interface_provider.InitWithNewPipeAndPassRemote());
   output_endpoint->BindForServiceWorker(std::move(provider_info));
   return host;
 }
diff --git a/content/browser/shared_storage/shared_storage_browsertest.cc b/content/browser/shared_storage/shared_storage_browsertest.cc
index 72fe7fe..56bcbb63 100644
--- a/content/browser/shared_storage/shared_storage_browsertest.cc
+++ b/content/browser/shared_storage/shared_storage_browsertest.cc
@@ -383,11 +383,11 @@
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
   std::string expected_error = base::StrCat(
-      {"a JavaScript error:\nError: Failed to load ",
+      {"a JavaScript error: \"Error: Failed to load ",
        https_server()
            ->GetURL("a.test", "/shared_storage/nonexistent_module.js")
            .spec(),
-       " HTTP status = 404 Not Found.\n"});
+       " HTTP status = 404 Not Found.\"\n"});
 
   EvalJsResult result = EvalJs(shell(), R"(
       sharedStorage.worklet.addModule('shared_storage/nonexistent_module.js');
@@ -407,12 +407,12 @@
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
   std::string expected_error = base::StrCat(
-      {"a JavaScript error:\nError: Unexpected redirect on ",
+      {"a JavaScript error: \"Error: Unexpected redirect on ",
        https_server()
            ->GetURL("a.test",
                     "/server-redirect?shared_storage/simple_module.js")
            .spec(),
-       ".\n"});
+       ".\"\n"});
 
   EvalJsResult result = EvalJs(shell(), R"(
       sharedStorage.worklet.addModule(
@@ -434,11 +434,11 @@
   WebContentsConsoleObserver console_observer(shell()->web_contents());
 
   std::string expected_error = base::StrCat(
-      {"a JavaScript error:\nError: ",
+      {"a JavaScript error: \"Error: ",
        https_server()
            ->GetURL("a.test", "/shared_storage/erroneous_module.js")
            .spec(),
-       ":6 Uncaught ReferenceError: undefinedVariable is not defined.\n"});
+       ":6 Uncaught ReferenceError: undefinedVariable is not defined.\"\n"});
 
   EvalJsResult result = EvalJs(shell(), R"(
       sharedStorage.worklet.addModule('shared_storage/erroneous_module.js');
@@ -465,8 +465,8 @@
     )"));
 
   std::string expected_error =
-      "a JavaScript error:\nError: sharedStorage.worklet.addModule() can only "
-      "be invoked once per browsing context.\n";
+      "a JavaScript error: \"Error: sharedStorage.worklet.addModule() can only "
+      "be invoked once per browsing context.\"\n";
 
   EvalJsResult result = EvalJs(shell(), R"(
       sharedStorage.worklet.addModule('shared_storage/simple_module.js');
@@ -573,12 +573,11 @@
     )");
 
   EXPECT_EQ(
-      std::string(
-          "a JavaScript error:\nError: function testFunction() {} could not be "
-          "cloned.\n    at eval (__const_std::string&_script__:4:21):\n        "
-          "         .then((result) => true ? result : Promise.reject(),\n      "
-          "                      ^^^^^\n    at eval (<anonymous>)\n    at "
-          "EvalJs-runner.js:2:34\n"),
+      std::string("a JavaScript error: \""
+                  "Error: function testFunction() {} could not be cloned.\n"
+                  "    at __const_std::string&_script__:4:21):\n"
+                  "              sharedStorage.run(\n"
+                  "                            ^^^^^\n"),
       result.error);
 }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b94e87b6..0ae2131 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3399,6 +3399,7 @@
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
   DCHECK(CanEnterFullscreenMode(requesting_frame, options));
   DCHECK(requesting_frame->IsActive());
+  DCHECK(ContainsOrIsFocusedWebContents());
 
   if (delegate_) {
     delegate_->EnterFullscreenModeForTab(requesting_frame, options);
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 781b7dd0..df80043e 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -549,16 +549,6 @@
 
   BeginRequestTimeout(options->timeout);
 
-  if (options->remote_desktop_client_override) {
-    // WebAuthRequestSecurityChecker will validate whether use of the extension
-    // is authorized.
-    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kWebAuthRemoteDesktopSupport)) {
-      mojo::ReportBadMessage("--webauthn-remote-desktop-support not enabled");
-      return;
-    }
-  }
-
   WebAuthRequestSecurityChecker::RequestType request_type =
       options->is_payment_credential_creation
           ? WebAuthRequestSecurityChecker::RequestType::kMakePaymentCredential
@@ -866,16 +856,6 @@
   DCHECK(get_assertion_response_callback_.is_null());
   get_assertion_response_callback_ = std::move(callback);
 
-  if (options->remote_desktop_client_override) {
-    // WebAuthRequestSecurityChecker will validate whether use of the extension
-    // is authorized.
-    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kWebAuthRemoteDesktopSupport)) {
-      mojo::ReportBadMessage("--webauthn-remote-desktop-support not enabled");
-      return;
-    }
-  }
-
   if (!options->is_conditional) {
     BeginRequestTimeout(options->timeout);
   }
diff --git a/content/browser/webui/web_ui_browsertest.cc b/content/browser/webui/web_ui_browsertest.cc
index e1d0ed9..310d7c77 100644
--- a/content/browser/webui/web_ui_browsertest.cc
+++ b/content/browser/webui/web_ui_browsertest.cc
@@ -653,8 +653,8 @@
       GetWebUIURL("test-host/web_ui_shared_worker.js"),
       kLoadSharedWorkerScript);
 
-  std::string expected_failure = R"(a JavaScript error:
-Error: Failed to construct 'SharedWorker')";
+  std::string expected_failure =
+      "a JavaScript error: \"Error: Failed to construct 'SharedWorker'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
 }
 
@@ -728,7 +728,7 @@
       kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'SharedWorker': "
+      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_shared_worker.js' cannot "
       "be accessed from origin 'chrome://trusted'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -746,7 +746,7 @@
       kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'SharedWorker': "
+      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_shared_worker.js' cannot "
       "be accessed from origin 'http://localhost";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -763,7 +763,7 @@
       GetWebUIURL("trusted/web_ui_shared_worker.js"), kLoadSharedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'SharedWorker': Script "
+      "a JavaScript error: \"Error: Failed to construct 'SharedWorker': Script "
       "at 'chrome://trusted/web_ui_shared_worker.js' cannot be accessed from "
       "origin 'chrome-untrusted://untrusted'.";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -792,8 +792,8 @@
       GURL(GetWebUIURL("test-host/web_ui_dedicated_worker.js")),
       kLoadDedicatedWorkerScript);
 
-  std::string expected_failure = R"(a JavaScript error:
-Error: Failed to construct 'Worker')";
+  std::string expected_failure =
+      "a JavaScript error: \"Error: Failed to construct 'Worker'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
 }
 
@@ -865,7 +865,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'Worker': "
+      "a JavaScript error: \"Error: Failed to construct 'Worker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_dedicated_worker.js' "
       "cannot be accessed from origin 'chrome://trusted'";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -883,7 +883,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'Worker': "
+      "a JavaScript error: \"Error: Failed to construct 'Worker': "
       "Script at 'chrome-untrusted://untrusted/web_ui_dedicated_worker.js' "
       "cannot be accessed from origin 'http://localhost";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
@@ -902,7 +902,7 @@
       kLoadDedicatedWorkerScript);
 
   std::string expected_failure =
-      "a JavaScript error:\nError: Failed to construct 'Worker': Script "
+      "a JavaScript error: \"Error: Failed to construct 'Worker': Script "
       "at 'chrome://trusted/web_ui_dedicated_worker.js' cannot be accessed "
       "from origin 'chrome-untrusted://untrusted'.";
   EXPECT_THAT(result.error, ::testing::StartsWith(expected_failure));
diff --git a/content/dev_ui_content_resources.grd b/content/dev_ui_content_resources.grd
index 601d735..eca74292 100644
--- a/content/dev_ui_content_resources.grd
+++ b/content/dev_ui_content_resources.grd
@@ -22,9 +22,11 @@
       <include name="IDR_ATTRIBUTION_INTERNALS_CSS" file="browser/resources/attribution_reporting/attribution_internals.css" type="BINDATA" />
       <include name="IDR_ATTRIBUTION_INTERNALS_MOJOM_JS" file="${root_gen_dir}/mojom-webui/content/browser/attribution_reporting/attribution_internals.mojom-webui.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_GPU_BROWSER_BRIDGE_JS" file="browser/resources/gpu/browser_bridge.js" type="BINDATA" />
-      <include name="IDR_GPU_INFO_VIEW_JS" file="browser/resources/gpu/info_view.js" type="BINDATA" />
-      <include name="IDR_GPU_INTERNALS_HTML" file="browser/resources/gpu/gpu_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+      <include name="IDR_GPU_INTERNALS_HTML" file="browser/resources/gpu/gpu_internals.html" type="BINDATA" />
       <include name="IDR_GPU_INTERNALS_JS" file="browser/resources/gpu/gpu_internals.js" type="BINDATA" />
+      <include name="IDR_GPU_INTERNALS_INFO_VIEW_JS" file="${root_gen_dir}/content/browser/resources/gpu/info_view.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_GPU_INTERNALS_INFO_VIEW_TABLE_JS" file="${root_gen_dir}/content/browser/resources/gpu/info_view_table.js" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_GPU_INTERNALS_INFO_VIEW_TABLE_ROW_JS" file="${root_gen_dir}/content/browser/resources/gpu/info_view_table_row.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_GPU_VULKAN_INFO_JS" file="browser/resources/gpu/vulkan_info.js" type="BINDATA" />
       <include name="IDR_INDEXED_DB_INTERNALS_HTML" file="browser/resources/indexed_db/indexeddb_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_INDEXED_DB_INTERNALS_JS" file="browser/resources/indexed_db/indexeddb_internals.js" type="BINDATA" />
diff --git a/content/gpu/gpu_sandbox_hook_linux.cc b/content/gpu/gpu_sandbox_hook_linux.cc
index 4475f9a..a89406e 100644
--- a/content/gpu/gpu_sandbox_hook_linux.cc
+++ b/content/gpu/gpu_sandbox_hook_linux.cc
@@ -104,6 +104,16 @@
 
 constexpr int dlopen_flag = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE;
 
+void AddStandardChromeOsPermissions(
+    std::vector<BrokerFilePermission>* permissions) {
+  static const char kAngleEglPath[] = "/opt/google/chrome/libEGL.so";
+  static const char kAngleGlesPath[] = "/opt/google/chrome/libGLESv2.so";
+
+  // For the ANGLE passthrough command decoder.
+  permissions->push_back(BrokerFilePermission::ReadOnly(kAngleEglPath));
+  permissions->push_back(BrokerFilePermission::ReadOnly(kAngleGlesPath));
+}
+
 void AddV4L2GpuPermissions(
     std::vector<BrokerFilePermission>* permissions,
     const sandbox::policy::SandboxSeccompBPF::Options& options) {
@@ -388,6 +398,7 @@
   AddVulkanICDPermissions(&permissions);
 
   if (IsChromeOS()) {
+    AddStandardChromeOsPermissions(&permissions);
     if (UseV4L2Codec())
       AddV4L2GpuPermissions(&permissions, options);
     if (IsArchitectureArm()) {
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index 985ee83..10ac5f92 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -25,6 +25,10 @@
 class StorageKey;
 }  // namespace blink
 
+namespace service_manager {
+class InterfaceProvider;
+}
+
 namespace url {
 class Origin;
 }  // namespace url
@@ -255,6 +259,16 @@
                                ServiceWorkerRunningInfo>&
   GetRunningServiceWorkerInfos() = 0;
 
+  // Returns the InterfaceProvider for the worker specified by
+  // `service_worker_version_id`. The caller can use InterfaceProvider to bind
+  // interfaces exposed by the Service Worker.
+  //
+  // Returns nullptr:
+  //  - if there is no service worker with |service_worker_version_id|
+  //  - during shutdown
+  virtual service_manager::InterfaceProvider* GetRemoteInterfaces(
+      int64_t service_worker_version_id) = 0;
+
  protected:
   ServiceWorkerContext() {}
   virtual ~ServiceWorkerContext() {}
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index d7091fc..ade9ed12e 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1598,7 +1598,7 @@
   // |source_frame_pattern| should match any line that looks like a stack frame
   // from a source file named |source_name|.
   const std::string source_frame_pattern =
-      base::StringPrintf("    at * (%s:*:*)", source_name.c_str());
+      base::StringPrintf("    at *%s:*:*", source_name.c_str());
 
   // This is the amount of indentation that is applied to the lines of inserted
   // annotations.
@@ -1668,93 +1668,149 @@
   return annotated_error.str();
 }
 
-EvalJsResult EvalRunnerScript(const ToRenderFrameHost& execution_target,
-                              const std::string& script,
-                              int options,
-                              int32_t world_id,
-                              const std::string& token) {
-  const char* kSourceURL = "__const_std::string&_script__";
-  bool use_automatic_reply = !(options & EXECUTE_SCRIPT_USE_MANUAL_REPLY);
-  bool user_gesture =
-      execution_target.render_frame_host()->GetLifecycleState() ==
-              RenderFrameHost::LifecycleState::kPrerendering
-          ? false
-          : !(options & EXECUTE_SCRIPT_NO_USER_GESTURE);
-  std::ostringstream error_stream;
-  std::unique_ptr<base::Value> response;
-  if (!execution_target.render_frame_host()->IsRenderFrameLive()) {
-    error_stream << "Error: EvalJs won't work on an already-crashed frame.";
-  } else if (!ExecuteScriptHelper(execution_target.render_frame_host(), script,
-                                  user_gesture, world_id, &response)) {
-    error_stream << "Internal Error: ExecuteScriptHelper failed";
-  } else if (!response) {
-    error_stream << "Internal Error: no value";
-  } else {
-    bool is_reply_from_script =
-        response->is_list() && response->GetListDeprecated().size() == 2 &&
-        response->GetListDeprecated()[0].is_string() &&
-        response->GetListDeprecated()[0].GetString() == token;
+// Waits for a response from ExecuteJavaScriptForTests, simulating an
+// error if the target renderer is destroyed while executing the script.
+class ExecuteJavaScriptForTestsWaiter : public WebContentsObserver {
+ public:
+  explicit ExecuteJavaScriptForTestsWaiter(const ToRenderFrameHost& adapter)
+      : WebContentsObserver(
+            WebContents::FromRenderFrameHost(adapter.render_frame_host())),
+        render_frame_host_(adapter.render_frame_host()) {}
 
-    bool is_error =
-        is_reply_from_script && response->GetListDeprecated()[1].is_string();
-    bool is_automatic_success_reply =
-        is_reply_from_script && response->GetListDeprecated()[1].is_list() &&
-        response->GetListDeprecated()[1].GetListDeprecated().size() == 1;
-
-    if (is_error) {
-      // This is a response generated by the error handler in our runner
-      // script. This occurs when the script throws an exception, or when
-      // eval throws a SyntaxError.
-      //
-      // Parse the stack trace here, and interleave lines of source code from
-      // |script| to aid debugging.
-      std::string error_text = response->GetListDeprecated()[1].GetString();
-
-      if (base::StartsWith(error_text,
-                           "a JavaScript error:\nEvalError: Refused",
-                           base::CompareCase::SENSITIVE)) {
-        error_text =
-            "EvalJs encountered an EvalError, because eval() is blocked by the "
-            "document's CSP on this page. To test content that is protected by "
-            "CSP, consider using EvalJs with an isolated world. Details: " +
-            error_text;
-      }
-
-      CHECK(!error_text.empty());
-      error_stream << AnnotateAndAdjustJsStackTraces(error_text, kSourceURL,
-                                                     script, 0);
-    } else if (!use_automatic_reply) {
-      // When |script| itself calls domAutomationController.send() on success,
-      // |response| could be anything; so there's no more checking we can do:
-      // return |response| as success, with an empty error.
-      return EvalJsResult(std::move(*response), std::string());
-    } else if (is_automatic_success_reply) {
-      // Got a response from the runner script that indicates success (of the
-      // form [token, [completion_value]]. Return the completion value, with an
-      // empty error.
-      return EvalJsResult(
-          std::move(response->GetListDeprecated()[1].GetListDeprecated()[0]),
-          std::string());
-    } else {
-      // The response was not well-formed (it failed the token match), so it's
-      // not from our runner script. Fail with an explanation of the raw
-      // message. This allows us to reject other calls
-      // domAutomationController.send().
-      error_stream
-          << "Internal Error: expected a 2-element list of the form "
-          << "['" << token << "', [result]]; but got instead: " << *response
-          << " ... This is potentially because a script tried to call "
-             "domAutomationController.send itself -- that is only allowed "
-             "when using EXECUTE_SCRIPT_USE_MANUAL_REPLY.  When using "
-             "EvalJs(), result values are just the result of calling eval() on "
-             "the script -- the completion value is the value of the last "
-             "executed statement.  When using ExecJs(), there is no result "
-             "value.";
-    }
+  blink::mojom::LocalFrame::JavaScriptExecuteRequestForTestsCallback
+  GetCallback() {
+    return base::BindOnce(&ExecuteJavaScriptForTestsWaiter::SetValue,
+                          weak_ptr_factory_.GetWeakPtr());
   }
 
-  // Something went wrong. Return an empty value and a non-empty error.
-  return EvalJsResult(base::Value(), error_stream.str());
+  bool Wait() {
+    if (!has_value_)
+      run_loop_.Run();
+    return has_value_;
+  }
+
+  blink::mojom::JavaScriptExecutionResultType GetResultType() {
+    DCHECK(has_value_);
+    return type_;
+  }
+
+  const base::Value& GetResult() {
+    DCHECK(has_value_);
+    return value_;
+  }
+
+  // WebContentsObserver
+  void PrimaryMainFrameRenderProcessGone(
+      base::TerminationStatus status) override {
+    if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
+        status == base::TERMINATION_STATUS_STILL_RUNNING) {
+      return;
+    }
+    RendererTerminated();
+  }
+  void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
+    if (render_frame_host_ != render_frame_host)
+      return;
+    RendererTerminated();
+  }
+
+ private:
+  void RendererTerminated() {
+    render_frame_host_ = nullptr;
+    if (has_value_)
+      return;
+    SetValue(blink::mojom::JavaScriptExecutionResultType::kException,
+             base::Value("Renderer terminated"));
+  }
+
+  void SetValue(blink::mojom::JavaScriptExecutionResultType type,
+                base::Value value) {
+    DCHECK(!has_value_);
+    has_value_ = true;
+    type_ = type;
+    value_ = value.Clone();
+    run_loop_.Quit();
+  }
+
+  RenderFrameHost* render_frame_host_;
+  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
+  bool has_value_ = false;
+  blink::mojom::JavaScriptExecutionResultType type_;
+  base::Value value_;
+
+  base::WeakPtrFactory<ExecuteJavaScriptForTestsWaiter> weak_ptr_factory_{this};
+};
+
+EvalJsResult EvalJsRunner(const ToRenderFrameHost& execution_target,
+                          const std::string& script,
+                          const std::string& source_url,
+                          int options,
+                          int32_t world_id) {
+  RenderFrameHostImpl* rfh =
+      static_cast<RenderFrameHostImpl*>(execution_target.render_frame_host());
+  if (!rfh->IsRenderFrameLive()) {
+    return EvalJsResult(
+        base::Value(), "Error: EvalJs won't work on an already-crashed frame.");
+  }
+
+  bool resolve_promises = !(options & EXECUTE_SCRIPT_NO_RESOLVE_PROMISES);
+  bool user_gesture = rfh->GetLifecycleState() !=
+                          RenderFrameHost::LifecycleState::kPrerendering &&
+                      !(options & EXECUTE_SCRIPT_NO_USER_GESTURE) &&
+                      world_id == ISOLATED_WORLD_ID_GLOBAL;
+
+  DOMMessageQueue dom_message_queue(rfh);
+  ExecuteJavaScriptForTestsWaiter waiter(rfh);
+  rfh->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script), user_gesture,
+                                 resolve_promises, world_id,
+                                 waiter.GetCallback());
+
+  bool has_value = waiter.Wait();
+  if (!has_value) {
+    return EvalJsResult(base::Value(),
+                        "Timeout waiting for Javascript to execute.");
+  }
+
+  using blink::mojom::JavaScriptExecutionResultType;
+  JavaScriptExecutionResultType result_type = waiter.GetResultType();
+  const base::Value& result_value = waiter.GetResult();
+
+  if (result_type == JavaScriptExecutionResultType::kException) {
+    // Parse the stack trace here, and interleave lines of source code from
+    // |script| to aid debugging.
+    CHECK(result_value.is_string() && !result_value.GetString().empty());
+    std::string error_text =
+        "a JavaScript error: \"" + result_value.GetString() + "\"";
+    return EvalJsResult(base::Value(), AnnotateAndAdjustJsStackTraces(
+                                           error_text, source_url, script, 0));
+  } else if (options & EXECUTE_SCRIPT_USE_MANUAL_REPLY) {
+    // Callers that set EXECUTE_SCRIPT_USE_MANUAL_REPLY expect this function to
+    // block until their JS calls `window.domAutomationController.send`. To
+    // support this, wait for a message from DOMMessageQueue and parse it as
+    // JSON.
+    std::string json;
+    if (!dom_message_queue.WaitForMessage(&json)) {
+      return EvalJsResult(base::Value(),
+                          "Cannot communicate with DOMMessageQueue.");
+    }
+
+    base::JSONReader::ValueWithError parsed_json =
+        base::JSONReader::ReadAndReturnValueWithError(
+            json, base::JSON_ALLOW_TRAILING_COMMAS);
+    if (!parsed_json.value)
+      return EvalJsResult(base::Value(), parsed_json.error_message);
+    result_type = JavaScriptExecutionResultType::kSuccess;
+    return EvalJsResult(parsed_json.value->Clone(), std::string());
+  } else if (dom_message_queue.HasMessages()) {
+    return EvalJsResult(base::Value(),
+                        "Calling domAutomationController.send is only allowed "
+                        "when using EXECUTE_SCRIPT_USE_MANUAL_REPLY. When "
+                        "using EvalJs(), the completion value is the value of "
+                        "the last executed statement. When using ExecJs(), "
+                        "there is no result value.");
+  }
+
+  return EvalJsResult(result_value.Clone(), std::string());
 }
 
 }  // namespace
@@ -1783,64 +1839,20 @@
                     int options,
                     int32_t world_id) {
   TRACE_EVENT1("test", "EvalJs", "script", script);
+
   // The sourceURL= parameter provides a string that replaces <anonymous> in
   // stack traces, if an Error is thrown. 'std::string' is meant to communicate
   // that this is a dynamic argument originating from C++ code.
+  //
+  // Wrapping the script in braces makes it run in a block scope so that
+  // let/const don't leak outside the code being run, but vars will float to
+  // the outer scope.
   const char* kSourceURL = "__const_std::string&_script__";
-  std::string modified_script =
-      base::StringPrintf("%s;\n//# sourceURL=%s", script.c_str(), kSourceURL);
+  std::string modified_script = base::StringPrintf("{%s\n}\n//# sourceURL=%s",
+                                                   script.c_str(), kSourceURL);
 
-  // An extra eval() indirection is used here to catch syntax errors and return
-  // them as assertion failures. This eval() operation deliberately occurs in
-  // the global scope, so 'var' declarations in |script| will persist for later
-  // script executions. (As an aside: global/local scope for eval depends on
-  // whether 'eval' is called directly or indirectly; 'window.eval()' is
-  // indirect).
-  //
-  // The call to eval() itself is inside a .then() handler so that syntax errors
-  // result in Promise rejection. Calling eval() either throws (in the event of
-  // a SyntaxError) or returns the script's completion value.
-  //
-  // The result of eval() (i.e., the statement completion value of |script|) is
-  // wrapped in an array and passed to a second .then() handler. If eval()
-  // returned a Promise and the |resolve_promises| option is set, this handler
-  // calls Promise.all to reply after the returned Promise resolves.
-  //
-  // If |script| evaluated successfully, the third.then() handler maps the
-  // resolved |result| of eval() to a |reply| that is a one-element list
-  // containing the value (this element can be any JSON-serializable type). If
-  // the manual reply option is being used, no reply is emitted after successful
-  // execution -- the script is expected to call send() itself. The call to
-  // Promise.reject() squelches this reply, and the final .then() handler is not
-  // called.
-  //
-  // If an uncaught error was thrown, or eval() returns a Promise that is
-  // rejected, the third .then() handler maps the |error| to a |reply| that is
-  // a string value.
-  //
-  // The fourth and final .then() handler passes the |reply| (whether
-  // successful or unsuccessful) to domAutomationController.send(), so that it's
-  // transmitted back here in browser process C++ land. A GUID token is also
-  // included, that protects against |script| directly calling
-  // domAutomationController.send() itself, which is disallowed in EvalJs.
-  bool use_automatic_reply = !(options & EXECUTE_SCRIPT_USE_MANUAL_REPLY);
-  bool resolve_promises = !(options & EXECUTE_SCRIPT_NO_RESOLVE_PROMISES);
-
-  std::string token = "EvalJs-" + base::GenerateGUID();
-  std::string runner_script = JsReplace(
-      R"(Promise.resolve($1)
-         .then(script => [window.eval(script)])
-         .then((result) => $2 ? Promise.all(result) : result )
-         .then((result) => $3 ? result : Promise.reject(),
-               (error) => 'a JavaScript error:' +
-                          (error && error.stack ? '\n' + error.stack
-                                                : ' "' + error + '"'))
-         .then((reply) => window.domAutomationController.send([$4, reply]));
-      //# sourceURL=EvalJs-runner.js)",
-      modified_script, resolve_promises, use_automatic_reply, token);
-
-  return EvalRunnerScript(execution_target, runner_script, options, world_id,
-                          token);
+  return EvalJsRunner(execution_target, modified_script, kSourceURL, options,
+                      world_id);
 }
 
 EvalJsResult EvalJsAfterLifecycleUpdate(
@@ -1849,10 +1861,11 @@
     const std::string& script,
     int options,
     int32_t world_id) {
-  bool use_automatic_reply = !(options & EXECUTE_SCRIPT_USE_MANUAL_REPLY);
-  bool resolve_promises = !(options & EXECUTE_SCRIPT_NO_RESOLVE_PROMISES);
-  std::string token = "EvalJs-" + base::GenerateGUID();
+  TRACE_EVENT2("test", "EvalJsAfterLifecycleUpdate", "raf_script", raf_script,
+               "script", script);
+
   const char* kSourceURL = "__const_std::string&_script__";
+  const char* kWrapperURL = "__const_std::string&_EvalJsAfterLifecycleUpdate__";
   std::string modified_raf_script;
   if (raf_script.length()) {
     modified_raf_script = base::StringPrintf("%s;\n//# sourceURL=%s",
@@ -1861,29 +1874,34 @@
   std::string modified_script =
       base::StringPrintf("%s;\n//# sourceURL=%s", script.c_str(), kSourceURL);
 
-  // This runner_script is very similar to that used by EvalJs, except that
-  // this one delays running the argument script until just before
+  // This runner_script delays running the argument scripts until just before
   // (|raf_script|) and after (|script|) a rendering update.
   std::string runner_script = JsReplace(
-      R"(Promise.all([$1, $2])
-         .then(scripts => new Promise((resolve, reject) => {
-               requestAnimationFrame(() => {
-                 window.eval(scripts[0]);
-                 setTimeout(() => {
-                   resolve([window.eval(scripts[1])])
-                 }) }) }) )
-         .then((result) => $3 ? Promise.all(result) : result )
-         .then((result) => $4 ? result : Promise.reject(),
-               (error) => 'a JavaScript error:' +
-                          (error && error.stack ? '\n' + error.stack
-                                                : ' "' + error + '"'))
-         .then((reply) => window.domAutomationController.send([$5, reply]));
-      //# sourceURL=EvalJs-runner.js)",
-      modified_raf_script, modified_script, resolve_promises,
-      use_automatic_reply, token);
+      R"(new Promise((resolve, reject) => {
+           requestAnimationFrame(() => {
+             try { window.eval($1); } catch (e) { reject(e); }
+             setTimeout(() => {
+               try { resolve(window.eval($2)); } catch (e) { reject(e); }
+             });
+           });
+         })
+         //# sourceURL=$3)",
+      modified_raf_script, modified_script, kWrapperURL);
 
-  return EvalRunnerScript(execution_target, runner_script, options, world_id,
-                          token);
+  EvalJsResult result = EvalJsRunner(execution_target, runner_script,
+                                     kWrapperURL, options, world_id);
+
+  if (base::StartsWith(result.error, "a JavaScript error: \"EvalError: Refused",
+                       base::CompareCase::SENSITIVE)) {
+    return EvalJsResult(
+        base::Value(),
+        "EvalJsAfterLifecycleUpdate encountered an EvalError, because eval() "
+        "is blocked by the document's CSP on this page. To test content that "
+        "is protected by CSP, consider using EvalJsAfterLifecycleUpdate in an "
+        "isolated world. Details: " +
+            result.error);
+  }
+  return result;
 }
 
 namespace {
@@ -2683,6 +2701,10 @@
   return true;
 }
 
+bool DOMMessageQueue::HasMessages() {
+  return !message_queue_.empty();
+}
+
 WebContentsAddedObserver::WebContentsAddedObserver()
     : web_contents_created_callback_(
           base::BindRepeating(&WebContentsAddedObserver::WebContentsCreated,
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 574e640..2d75623 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1276,6 +1276,9 @@
   // true.  Otherwise (if the queue is empty), returns false.
   [[nodiscard]] bool PopMessage(std::string* message);
 
+  // Returns true if there are currently any messages in the queue.
+  bool HasMessages();
+
   // Overridden NotificationObserver methods.
   void Observe(int type,
                const NotificationSource& source,
diff --git a/content/public/test/fake_service_worker_context.cc b/content/public/test/fake_service_worker_context.cc
index dc541ba..f568bae9 100644
--- a/content/public/test/fake_service_worker_context.cc
+++ b/content/public/test/fake_service_worker_context.cc
@@ -101,6 +101,14 @@
     ServiceWorkerContext::StatusCodeCallback failure_callback) {
   NOTREACHED();
 }
+
+service_manager::InterfaceProvider*
+FakeServiceWorkerContext::GetRemoteInterfaces(
+    int64_t service_worker_version_id) {
+  NOTREACHED();
+  return nullptr;
+}
+
 void FakeServiceWorkerContext::StartServiceWorkerForNavigationHint(
     const GURL& document_url,
     const blink::StorageKey& key,
diff --git a/content/public/test/fake_service_worker_context.h b/content/public/test/fake_service_worker_context.h
index ffecd40..32b9d04 100644
--- a/content/public/test/fake_service_worker_context.h
+++ b/content/public/test/fake_service_worker_context.h
@@ -80,6 +80,8 @@
       const blink::StorageKey& key,
       ServiceWorkerContext::StartWorkerCallback info_callback,
       ServiceWorkerContext::StatusCodeCallback failure_callback) override;
+  service_manager::InterfaceProvider* GetRemoteInterfaces(
+      int64_t service_worker_version_id) override;
   void StartServiceWorkerAndDispatchMessage(
       const GURL& scope,
       const blink::StorageKey& key,
diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc
index aa30f0c..b674a89 100644
--- a/content/public/test/test_renderer_host.cc
+++ b/content/public/test/test_renderer_host.cc
@@ -40,9 +40,12 @@
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
+#include "ui/display/screen.h"
+#endif
+
 #if BUILDFLAG(IS_ANDROID)
 #include "ui/android/dummy_screen_android.h"
-#include "ui/display/screen.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
@@ -239,6 +242,10 @@
 #if BUILDFLAG(IS_WIN)
   ole_initializer_ = std::make_unique<ui::ScopedOleInitializer>();
 #endif
+#if BUILDFLAG(IS_MAC)
+  screen_ = std::make_unique<display::ScopedNativeScreen>();
+#endif
+
 #if defined(USE_AURA)
   aura_test_helper_ = std::make_unique<aura::test::AuraTestHelper>(
       ImageTransportFactory::GetInstance()->GetContextFactory());
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h
index a02288f..8741365c 100644
--- a/content/public/test/test_renderer_host.h
+++ b/content/public/test/test_renderer_host.h
@@ -43,6 +43,7 @@
 
 namespace display {
 class Screen;
+class ScopedNativeScreen;
 }
 
 namespace net {
@@ -325,6 +326,9 @@
 #if BUILDFLAG(IS_WIN)
   std::unique_ptr<ui::ScopedOleInitializer> ole_initializer_;
 #endif
+#if BUILDFLAG(IS_MAC)
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
+#endif
 #if defined(USE_AURA)
   std::unique_ptr<aura::test::AuraTestHelper> aura_test_helper_;
 #endif
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index f0c8e7e..7e445f5 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -93,7 +93,8 @@
       std::move(params->renderer_preferences),
       std::move(params->service_worker_receiver),
       std::move(params->controller_receiver), std::move(params->instance_host),
-      std::move(params->provider_info), this, std::move(start_timing),
+      std::move(params->interface_provider), std::move(params->provider_info),
+      this, std::move(start_timing),
       std::move(params->preference_watcher_receiver),
       std::move(params->subresource_loader_factories),
       std::move(params->subresource_loader_updater),
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index cd1cfbd2..a069626 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -102,6 +102,8 @@
         controller_receiver,
     mojo::PendingAssociatedRemote<blink::mojom::EmbeddedWorkerInstanceHost>
         instance_host,
+    mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
+        pending_interface_provider_receiver,
     blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
     EmbeddedWorkerInstanceClientImpl* owner,
     blink::mojom::EmbeddedWorkerStartTimingPtr start_timing,
@@ -125,6 +127,8 @@
       proxy_(nullptr),
       pending_service_worker_receiver_(std::move(service_worker_receiver)),
       controller_receiver_(std::move(controller_receiver)),
+      pending_interface_provider_receiver_(
+          std::move(pending_interface_provider_receiver)),
       pending_subresource_loader_updater_(
           std::move(subresource_loader_updater)),
       owner_(owner),
@@ -201,6 +205,13 @@
   return *worker_;
 }
 
+void ServiceWorkerContextClient::GetInterface(
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  if (registry_.TryBindInterface(interface_name, &interface_pipe))
+    return;
+}
+
 void ServiceWorkerContextClient::WorkerReadyForInspectionOnInitiatorThread(
     blink::CrossVariantMojoRemote<blink::mojom::DevToolsAgentInterfaceBase>
         devtools_agent_remote,
@@ -254,6 +265,10 @@
   DCHECK(controller_receiver_.is_valid());
   proxy_->BindControllerServiceWorker(std::move(controller_receiver_));
 
+  DCHECK(pending_interface_provider_receiver_.is_valid());
+  interface_provider_receiver_.Bind(
+      std::move(pending_interface_provider_receiver_));
+
   GetContentClient()
       ->renderer()
       ->DidInitializeServiceWorkerContextOnWorkerThread(
@@ -320,6 +335,14 @@
     v8::Local<v8::Context> context) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
 
+  // After WillDestroyWorkerContext is called, the ServiceWorkerContext
+  // is destroyed, so destroy InterfaceProvider here and clear the
+  // BinderRegistry to stop any future interface requests. InterfaceProvider is
+  // bound on the worker task runner and therefore, should be destroyed on the
+  // worker task runner.
+  interface_provider_receiver_.reset();
+  registry_.clear();
+
   // At this point WillStopCurrentWorkerThread is already called, so
   // worker_task_runner_->RunsTasksInCurrentSequence() returns false
   // (while we're still on the worker thread).
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 188e0ef..b5e0c76 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -23,6 +23,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/shared_associated_remote.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom-forward.h"
 #include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
@@ -71,7 +72,9 @@
 //
 // Unless otherwise noted (here or in base class documentation), all methods
 // are called on the worker thread.
-class ServiceWorkerContextClient : public blink::WebServiceWorkerContextClient {
+class ServiceWorkerContextClient
+    : public blink::WebServiceWorkerContextClient,
+      public service_manager::mojom::InterfaceProvider {
  public:
   // Called on the initiator thread.
   // - |is_starting_installed_worker| is true if the script is already installed
@@ -99,6 +102,8 @@
           controller_receiver,
       mojo::PendingAssociatedRemote<blink::mojom::EmbeddedWorkerInstanceHost>
           instance_host,
+      mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
+          interface_provider,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       EmbeddedWorkerInstanceClientImpl* owner,
       blink::mojom::EmbeddedWorkerStartTimingPtr start_timing,
@@ -133,6 +138,10 @@
   // Called on the initiator thread.
   blink::WebEmbeddedWorker& worker();
 
+  // service_manager::mojom::InterfaceProvider:
+  void GetInterface(const std::string& interface_name,
+                    mojo::ScopedMessagePipeHandle interface_pipe) override;
+
   // WebServiceWorkerContextClient overrides.
   void WorkerReadyForInspectionOnInitiatorThread(
       blink::CrossVariantMojoRemote<blink::mojom::DevToolsAgentInterfaceBase>
@@ -229,6 +238,8 @@
       pending_service_worker_receiver_;
   mojo::PendingReceiver<blink::mojom::ControllerServiceWorker>
       controller_receiver_;
+  mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
+      pending_interface_provider_receiver_;
   mojo::PendingReceiver<blink::mojom::SubresourceLoaderUpdater>
       pending_subresource_loader_updater_;
 
@@ -236,6 +247,12 @@
   service_manager::BinderRegistry registry_;
   std::unique_ptr<BlinkInterfaceRegistryImpl> blink_interface_registry_;
 
+  // Receiver for the InterfaceProvider interface which is used by the browser
+  // to request interfaces that are exposed by the renderer. Bound and destroyed
+  // on the worker task runner.
+  mojo::Receiver<service_manager::mojom::InterfaceProvider>
+      interface_provider_receiver_{this};
+
   // This is bound on the initiator thread.
   mojo::SharedAssociatedRemote<blink::mojom::EmbeddedWorkerInstanceHost>
       instance_host_;
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 15cbdd3..2ee2fdb 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -310,6 +310,10 @@
     deps += [ "//ui/views/linux_ui:linux_ui_factory" ]
   }
 
+  if (is_mac) {
+    deps += [ "//ui/display:test_support" ]
+  }
+
   if (is_android) {
     deps += [
       "//components/embedder_support/android:view",
diff --git a/content/shell/browser/shell_platform_data_aura.cc b/content/shell/browser/shell_platform_data_aura.cc
index a02a4058..6e2480ed 100644
--- a/content/shell/browser/shell_platform_data_aura.cc
+++ b/content/shell/browser/shell_platform_data_aura.cc
@@ -82,12 +82,7 @@
 
 #if defined(USE_OZONE)
   // Setup global display::Screen singleton.
-  if (!display::Screen::GetScreen()) {
-    std::unique_ptr<aura::ScreenOzone> screen_ozone =
-        std::make_unique<aura::ScreenOzone>();
-    screen_ozone.get()->Initialize();
-    screen_ = std::move(screen_ozone);
-  }
+  screen_ = std::make_unique<aura::ScopedScreenOzone>();
 #endif  // defined(USE_OZONE)
 
   ui::PlatformWindowInitProperties properties;
diff --git a/content/shell/browser/shell_platform_data_aura.h b/content/shell/browser/shell_platform_data_aura.h
index 8b58302..345a9d9 100644
--- a/content/shell/browser/shell_platform_data_aura.h
+++ b/content/shell/browser/shell_platform_data_aura.h
@@ -20,7 +20,7 @@
 
 #if defined(USE_OZONE)
 namespace display {
-class Screen;
+class ScopedNativeScreen;
 }
 #endif
 
@@ -46,7 +46,7 @@
 
  private:
 #if defined(USE_OZONE)
-  std::unique_ptr<display::Screen> screen_;
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
 #endif
 
   std::unique_ptr<aura::WindowTreeHost> host_;
diff --git a/content/shell/browser/shell_platform_delegate.h b/content/shell/browser/shell_platform_delegate.h
index 32cd23a..9326cb3 100644
--- a/content/shell/browser/shell_platform_delegate.h
+++ b/content/shell/browser/shell_platform_delegate.h
@@ -15,6 +15,7 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "content/public/browser/native_web_keyboard_event.h"
+#include "ui/display/screen.h"
 #endif
 
 class GURL;
@@ -139,6 +140,9 @@
 #endif
 
  private:
+#if BUILDFLAG(IS_MAC)
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
+#endif
   // Data held for each Shell instance, since there is one ShellPlatformDelegate
   // for the whole browser process (shared across Shells). This is defined for
   // each platform implementation.
diff --git a/content/shell/browser/shell_platform_delegate_mac.mm b/content/shell/browser/shell_platform_delegate_mac.mm
index 0868a06..cf91694 100644
--- a/content/shell/browser/shell_platform_delegate_mac.mm
+++ b/content/shell/browser/shell_platform_delegate_mac.mm
@@ -134,7 +134,7 @@
 ShellPlatformDelegate::~ShellPlatformDelegate() = default;
 
 void ShellPlatformDelegate::Initialize(const gfx::Size& default_window_size) {
-  // |platform_| is unused on this platform.
+  screen_ = std::make_unique<display::ScopedNativeScreen>();
 }
 
 void ShellPlatformDelegate::CreatePlatformWindow(
diff --git a/content/shell/browser/shell_platform_delegate_views.cc b/content/shell/browser/shell_platform_delegate_views.cc
index f6924fc..0063882 100644
--- a/content/shell/browser/shell_platform_delegate_views.cc
+++ b/content/shell/browser/shell_platform_delegate_views.cc
@@ -331,8 +331,9 @@
       std::make_unique<wm::WMTestHelper>(default_window_size);
 #else
   platform_->wm_state = std::make_unique<wm::WMState>();
-  CHECK(!display::Screen::GetScreen());
-  platform_->screen = views::CreateDesktopScreen();
+  // FakeScreen tests create their own screen.
+  if (!display::Screen::HasScreen())
+    platform_->screen = views::CreateDesktopScreen();
 #endif
 
   platform_->views_delegate =
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c547597..f3f13ab 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1621,6 +1621,7 @@
     "//ui/base/ime/mojom",
     "//ui/compositor",
     "//ui/display",
+    "//ui/display:test_support",
     "//ui/events:test_support",
     "//ui/events/blink:blink",
     "//ui/gfx",
diff --git a/content/test/browser_test_utils_browsertest.cc b/content/test/browser_test_utils_browsertest.cc
index b4aaf66..d259083 100644
--- a/content/test/browser_test_utils_browsertest.cc
+++ b/content/test/browser_test_utils_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/scoped_run_loop_timeout.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -11,9 +12,12 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
 
 namespace content {
 
+using ::testing::Eq;
+
 class NavigationObserver: public WebContentsObserver {
  public:
   explicit NavigationObserver(WebContents* web_contents)
@@ -80,9 +84,7 @@
 
 using EvalJsBrowserTest = ContentBrowserTest;
 
-// TODO(mslekova): Re-enable once test expectations are updated,
-// see chromium:916975
-IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, DISABLED_EvalJsErrors) {
+IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, EvalJsErrors) {
   ASSERT_TRUE(embedded_test_server()->Start());
   EXPECT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
@@ -98,11 +100,8 @@
     EXPECT_FALSE("}}" != result);  // As should EXPECT_NE
     EXPECT_FALSE(nullptr == result);
 
-    std::string expected_error = R"(a JavaScript error:
-SyntaxError: Unexpected token }
-    at eval (<anonymous>)
-    at Promise.resolve.then.script (EvalJs-runner.js:2:34)
-)";
+    std::string expected_error =
+        "a JavaScript error: \"SyntaxError: Unexpected token '}'\"\n";
     EXPECT_FALSE(expected_error == result);
     EXPECT_EQ(expected_error, result.error);
   }
@@ -114,13 +113,10 @@
     EXPECT_FALSE(1 == result);
     EXPECT_FALSE("whoops" == result);
 
-    std::string expected_error = R"(a JavaScript error:
-Error: whoops
-    at eval (__const_std::string&_script__:1:11):
-        55; throw new Error('whoops');
-                  ^^^^^
-    at eval (<anonymous>)
-    at Promise.resolve.then.script (EvalJs-runner.js:2:34)
+    std::string expected_error = R"(a JavaScript error: "Error: whoops
+    at __const_std::string&_script__:1:12):
+        {55; throw new Error('whoops');
+                   ^^^^^
 )";
     EXPECT_FALSE(expected_error == result);
     EXPECT_EQ(expected_error, result.error);
@@ -136,19 +132,72 @@
     EXPECT_FALSE(22 == result);
     EXPECT_FALSE("sweet" == result);
 
-    std::string expected_error = R"(a JavaScript error:
-ReferenceError: z is not defined
-    at eval (__const_std::string&_script__:4:13):
-            var y = z + x;
-                    ^^^^^
-    at eval (<anonymous>)
-    at Promise.resolve.then.script (EvalJs-runner.js:2:34)
-)";
+    std::string expected_error =
+        "a JavaScript error: \"ReferenceError: z is not defined\n"
+        "    at __const_std::string&_script__:4:13):\n"
+        "            var y = z + x;\n"
+        "                    ^^^^^\n";
     EXPECT_FALSE(expected_error == result);
     EXPECT_EQ(expected_error, result.error);
   }
 }
 
+IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, EvalJsAfterLifecycleUpdateErrors) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
+
+  {
+    // Test syntax errors.
+    auto result = EvalJsAfterLifecycleUpdate(shell(), "}}", "'hi'");
+
+    EXPECT_TRUE(result.value.is_none());
+    EXPECT_THAT(
+        result.error,
+        Eq("a JavaScript error: \"SyntaxError: Unexpected token '}'\n"
+           "    at eval (<anonymous>)\n"
+           "    at \"__const_std::string&_EvalJsAfterLifecycleUpdate__\""
+           ":3:27\"\n"));
+
+    auto result2 = EvalJsAfterLifecycleUpdate(shell(), "'hi'", "]]");
+
+    EXPECT_TRUE(result2.value.is_none());
+    EXPECT_THAT(
+        result2.error,
+        Eq("a JavaScript error: \"SyntaxError: Unexpected token ']'\n"
+           "    at eval (<anonymous>)\n"
+           "    at \"__const_std::string&_EvalJsAfterLifecycleUpdate__\""
+           ":5:37\"\n"));
+  }
+
+  {
+    // Test throwing exceptions.
+    auto result = EvalJsAfterLifecycleUpdate(
+        shell(), "55; throw new Error('whoops');", "'hi'");
+
+    EXPECT_TRUE(result.value.is_none());
+    EXPECT_THAT(
+        result.error,
+        Eq("a JavaScript error: \"Error: whoops\n"
+           "    at eval (__const_std::string&_script__:1:11)\n"
+           "    at eval (<anonymous>)\n"
+           "    at \"__const_std::string&_EvalJsAfterLifecycleUpdate__\""
+           ":3:27\"\n"));
+
+    auto result2 = EvalJsAfterLifecycleUpdate(
+        shell(), "'hi'", "55; throw new Error('whoopsie');");
+
+    EXPECT_TRUE(result2.value.is_none());
+    EXPECT_THAT(
+        result2.error,
+        Eq("a JavaScript error: \"Error: whoopsie\n"
+           "    at eval (__const_std::string&_script__:1:11)\n"
+           "    at eval (<anonymous>)\n"
+           "    at \"__const_std::string&_EvalJsAfterLifecycleUpdate__\""
+           ":5:37\"\n"));
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, EvalJsWithManualReply) {
   ASSERT_TRUE(embedded_test_server()->Start());
   EXPECT_TRUE(
@@ -165,18 +214,58 @@
   EXPECT_FALSE(20 == result);
   EXPECT_FALSE("hi" == result);
   EXPECT_THAT(result.error,
-              ::testing::StartsWith(
-                  "Internal Error: expected a 2-element list of the form "));
+              ::testing::EndsWith(
+                  "Calling domAutomationController.send is only allowed "
+                  "when using EXECUTE_SCRIPT_USE_MANUAL_REPLY. When "
+                  "using EvalJs(), the completion value is the value of "
+                  "the last executed statement. When using ExecJs(), "
+                  "there is no result value."));
+}
+
+IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, EvalJsTimeout) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
+
+  base::test::ScopedRunLoopTimeout scoped_run_timeout(FROM_HERE,
+                                                      base::Milliseconds(1));
+
+  // Store the promise resolve function so it doesn't get GC'd.
+  static std::string script = "new Promise(resolve => {window.r = resolve})";
+  static std::string error;
+  static Shell* shell_ptr = shell();
+  EXPECT_FATAL_FAILURE(error = EvalJs(shell_ptr, script).error,
+                       "RunLoop::Run() timed out.");
+
+  EXPECT_THAT(error, Eq("Timeout waiting for Javascript to execute."));
+}
+
+IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest, EvalJsNotBlockedByCSP) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL(
+                   "/set-header?Content-Security-Policy: script-src 'self'")));
+
+  auto result = EvalJs(shell(), "'hi'");
+  EXPECT_EQ("hi", result);
+}
+
+IN_PROC_BROWSER_TEST_F(EvalJsBrowserTest,
+                       EvalJsAfterLifecycleUpdateBlockedByCSP) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL(
+                   "/set-header?Content-Security-Policy: script-src 'self'")));
+
+  auto result = EvalJsAfterLifecycleUpdate(shell(), "'hi'", "");
+  EXPECT_TRUE(result.value.is_none());
   EXPECT_THAT(
       result.error,
-      ::testing::EndsWith("This is potentially because a script tried to call "
-                          "domAutomationController.send itself -- that is only "
-                          "allowed when using "
-                          "EXECUTE_SCRIPT_USE_MANUAL_REPLY.  When using "
-                          "EvalJs(), result values are just the result of "
-                          "calling eval() on the script -- the completion "
-                          "value is the value of the last executed statement.  "
-                          "When using ExecJs(), there is no result value."));
+      ::testing::StartsWith(
+          "EvalJsAfterLifecycleUpdate encountered an EvalError, because eval() "
+          "is blocked by the document's CSP on this page. To test content that "
+          "is protected by CSP, consider using EvalJsAfterLifecycleUpdate in "
+          "an isolated world. Details:"));
 }
 
 }  // namespace content
diff --git a/content/test/gpu/gpu_tests/context_lost_integration_test.py b/content/test/gpu/gpu_tests/context_lost_integration_test.py
index 2133193..5cec51e7 100644
--- a/content/test/gpu/gpu_tests/context_lost_integration_test.py
+++ b/content/test/gpu/gpu_tests/context_lost_integration_test.py
@@ -57,11 +57,12 @@
 feature_query_script = """
   function GetFeatureStatus(feature_name, for_hardware_gpu) {
     let query_result;
+    const infoView = document.querySelector('info-view');
     if (for_hardware_gpu) {
-      query_result = document.querySelector(
+      query_result = infoView.shadowRoot.querySelector(
           '.feature-status-for-hardware-gpu-list');
     } else {
-      query_result = document.querySelector('.feature-status-list');
+      query_result = infoView.shadowRoot.querySelector('.feature-status-list');
     }
     for (let i = 0; i < query_result.childElementCount; i++) {
       let feature_status = query_result.children[i].textContent.split(': ');
@@ -75,17 +76,20 @@
 vendor_id_query_script = """
   function GetActiveVendorId(for_hardware_gpu) {
     let div;
+    const infoView = document.querySelector('info-view');
     if (for_hardware_gpu) {
-      div = document.querySelector('.basic-info-for-hardware-gpu-div');
+      div = infoView.shadowRoot.querySelector(
+          '.basic-info-for-hardware-gpu-div');
     } else {
-      div = document.querySelector('#basic-info');
+      div = infoView.shadowRoot.querySelector('#basic-info');
     }
-    let trs = div.getElementsByTagName('tr');
+    const table = div.querySelector('info-view-table');
+    let trs = table.shadowRoot.querySelectorAll('info-view-table-row');
     let vendor_id = 0;
     // The first four rows are "Initialization time", "In-process GPU",
     // "Passthrough Command Decoder", and "Sandboxed".
     for (let i = 4; i < trs.length; i++) {
-      let tds = trs[i].getElementsByTagName('td');
+      let tds = trs[i].shadowRoot.querySelectorAll('div');
       let token = tds[0].textContent.trim();
       if (!token.startsWith('GPU'))
         break;
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index fc7bde7..1d09c233 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -38,7 +38,8 @@
   window.domAutomationController = domAutomationController;
 
   function GetDriverBugWorkarounds() {
-    var query_result = document.querySelector('.workarounds-list');
+    var query_result = document.querySelector('info-view').shadowRoot
+        .querySelector('.workarounds-list');
     var browser_list = []
     for (var i=0; i < query_result.childElementCount; i++)
       browser_list.push(query_result.children[i].textContent);
diff --git a/content/test/gpu/gpu_tests/hardware_accelerated_feature_integration_test.py b/content/test/gpu/gpu_tests/hardware_accelerated_feature_integration_test.py
index e68c93e..35e8e9a 100644
--- a/content/test/gpu/gpu_tests/hardware_accelerated_feature_integration_test.py
+++ b/content/test/gpu/gpu_tests/hardware_accelerated_feature_integration_test.py
@@ -15,7 +15,8 @@
 test_harness_script = r"""
   function VerifyHardwareAccelerated(feature) {
     feature += ': '
-    var list = document.querySelector('.feature-status-list');
+    var list = document.querySelector('info-view').shadowRoot.querySelector(
+        '.feature-status-list');
     for (var i=0; i < list.childElementCount; i++) {
       var span_list = list.children[i].getElementsByTagName('span');
       var feature_str = span_list[0].textContent;
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 9947f97..800b97413 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -423,7 +423,8 @@
 # Intel OpenGL driver issue, fixed in 27.20.100.9126
 crbug.com/angleproject/5543 [ win intel angle-opengl passthrough ] conformance/extensions/webgl-compressed-texture-s3tc-srgb.html [ Failure ]
 
-# Note that the following test seems to pass, but it may still be flaky.
+# Consistent failures in functions.control_flow.return_in_nested_loop_vertex and functions.control_flow.return_in_nested_loop_fragment
+# Possible HLSL compiler bug.
 crbug.com/478572 [ win angle-d3d9 passthrough ] deqp/data/gles2/shaders/functions.html [ Failure ]
 
 # Win / AMD D3D11 (default) failures
diff --git a/content/web_test/BUILD.gn b/content/web_test/BUILD.gn
index 7aab966..00cce83 100644
--- a/content/web_test/BUILD.gn
+++ b/content/web_test/BUILD.gn
@@ -208,6 +208,14 @@
       "//ppapi:blink_test_plugin",
     ]
   }
+
+  if (is_mac) {
+    deps += [ "//ui/display:test_support" ]
+  }
+
+  if (use_aura) {
+    deps += [ "//ui/aura:test_support" ]
+  }
 }
 
 static_library("web_test_renderer") {
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.cc b/device/bluetooth/floss/bluetooth_adapter_floss.cc
index a07af06..0651298 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.cc
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.cc
@@ -122,7 +122,7 @@
   if (!FlossDBusManager::Get()->HasActiveAdapter())
     return;
 
-  devices_.clear();
+  ClearAllDevices();
 
   // Remove adapter by switching to an invalid adapter (cleans up DBus clients)
   // and then emitting |AdapterPresentChanged| to observers.
@@ -136,6 +136,19 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
+void BluetoothAdapterFloss::ClearAllDevices() {
+  // Move all elements of the original devices list to a new list here,
+  // leaving the original list empty so that when we send DeviceRemoved(),
+  // GetDevices() returns no devices.
+  DevicesMap devices_swapped;
+  devices_swapped.swap(devices_);
+
+  for (auto& iter : devices_swapped) {
+    for (auto& observer : observers_)
+      observer.DeviceRemoved(this, iter.second.get());
+  }
+}
+
 void BluetoothAdapterFloss::Init() {
   // If dbus is shutdown or ObjectManager isn't supported, we just return
   // without initializing anything.
@@ -511,7 +524,7 @@
   if (enabled) {
     PopulateInitialDevices();
   } else {
-    devices_.clear();
+    ClearAllDevices();
   }
 
   NotifyAdapterPoweredChanged(enabled);
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.h b/device/bluetooth/floss/bluetooth_adapter_floss.h
index 930b32d..d107b11 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.h
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.h
@@ -183,6 +183,7 @@
   void RemoveAdapter();
 
   void PopulateInitialDevices();
+  void ClearAllDevices();
 
   // floss::FlossAdapterClient::Observer override.
   void DiscoverableChanged(bool discoverable) override;
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 11e58079..2cec12ca 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -9,10 +9,12 @@
 [TOC]
 
 ## Metainstaller
-The metainstaller (UpdaterSetup) is a thin executable that contains a compressed
-copy of the updater as a resource, extracts it, and triggers installation of the
-updater / an app. The metainstaller is downloaded by the user and can be run
-from any directory.
+The metainstaller (UpdaterSetup) is a small executable that contains a
+compressed copy of the updater as a resource, extracts it, and triggers
+installation of the updater / an app. The metainstaller is downloaded by the
+user and can be run from any directory.
+
+The size of the metainstaller is less than 1500KiB.
 
 The metainstaller may have a tag attached to it. The tag is a piece of unsigned
 data from which the metainstaller extracts the ID of the application to be
@@ -31,25 +33,37 @@
 preferred language on the current system. Every string shown in the UI is
 translated.
 
-## Standalone Installer
-TODO(crbug.com/1035895): Document the standalone installer.
+## Bundle Installer
+The bundle installer allows installation of more than one application. The
+bundle installer is typically used in software distribution scenarios.
 
 TODO(crbug.com/1035895): Document bundled installers.
 
+
+## Standalone Installer
+TODO(crbug.com/1035895): Document the standalone installer, including building
+a standalone installer for a given application.
+
 Applications on macOS frequently install via "drag-install", and then install
 the updater using a standalone installer on the application's first-run. The
 updater app can be embedded in a macOS application bundle as a helper and then
 invoked with appropriate command line arguments to install itself.
 
+## MSI Wrapper
+TODO(crbug.com/1327497) - document.
+
+
 ## Updater
 The updater is installed at:
-*   (Windows, User): `%LOCAL_APP_DATA%\{COMPANY}\{UPDATERNAME}\{VERSION}\updater.exe`
-*   (Windows, System): `%PROGRAM_FILES%\{COMPANY}\{UPDATERNAME}\{VERSION}\updater.exe`
-*   (macOS, User): `~/Library/{COMPANY}/{UPDATERNAME}/{VERSION}/{UPDATERNAME}.app`
-*   (macOS, System): `/Library/{COMPANY}/{UPDATERNAME}/{VERSION}/{UPDATERNAME}.app`
+
+* (Windows, User): `%LOCAL_APP_DATA%\{COMPANY}\{UPDATERNAME}\{VERSION}\updater.exe`
+* (Windows, System): `%PROGRAM_FILES%\{COMPANY}\{UPDATERNAME}\{VERSION}\updater.exe`
+* (macOS, User): `~/Library/{COMPANY}/{UPDATERNAME}/{VERSION}/{UPDATERNAME}.app`
+* (macOS, System): `/Library/{COMPANY}/{UPDATERNAME}/{VERSION}/{UPDATERNAME}.app`
 
 The updater's functionality is split between several processes. The mode of a
 process is determined by command-line arguments:
+
 *   --install [--app-id=...]
     *   Install and activate this version of the updater if there is no active
         updater.
@@ -137,9 +151,80 @@
     *   The updater operates in system scope if and only if this switch is
         present.
 
+### Protocol
+The updater communicates with update servers using the
+[Omaha Protocol](protocol_3_1.md).
+
+### Update Formats
+The updater accepts updates packaged as CRX₃ files. All files must be signed
+with a publisher key. The corresponding public key is hardcoded into the
+updater.
+
 ### Installation
+
+The updater must handle the installation of new applications. The updater can
+download, install, and update an applications when the application is running.
+
+One or more applications can be installed at once, as a bundle.
+
+Before installing, the integrity of the payload if verified. The payloads are
+stored in secure locations of the file system for per-system installs.
+
+
+Considering network connectivity, there are two scenarios for installing an
+application:
+
+1. Online
+2. Offline
+
+#### Online Installs
+
+An online install is done with a [metainstaller](#Metainstaller). Every time an
+online installer is run, it sends an install event ping. The update check and
+the install event ping have the same session id. The install ping is lost if the
+network is unreachable for any reason, or the program has crashed.
+
+#### Standalone/Offline Installs
+
+This type of installs is done with a [standalone/offline installer]
+(#Standalone-Installer). This is an installer which embeds all data required to
+install the application, including the payload and various configuration data
+needed by the application setup. Such an install must be able to complete when
+a network connection is not available.
+
+There are a couple of scenarios where the standalone/offline installer is used:
+
+1. when an interactive user experience is not needed, such as automated
+deployments in the enterprise.
+2. when downloading the application payload is not desirable for any reason.
+3. OEM installs.
+
+[installdataindex](#installdataindex) provides a mechanism to specify
+configuration data which is passed to the application installer.
+
+#### User Interface
 TODO(crbug.com/1035895): Document UI/UX
 
+* The install flow can be stopped before the payload finished downloading.
+* The user interface is localized in the following languages: TBD.
+* Has a silent mode where the UI is not displayed at all.
+
+##### Help Button
+If the installation fails, the updater shows an error message with a "Help"
+button. Clicking the help button opens a web page in the user's default browser.
+The page is opened with a query string:
+`?product={AppId}&errorcode={ErrorCode}`.
+
+#### OEM Installs.
+
+TODO(crbug.com/1139014): Document OEM.
+
+#### Install Source
+
+The `installsource` identifies the originator of an install. It is provided on
+the command line of the metainstaller.
+TODO(crbug.com/1327491) - is this needed? If yes, document the algorithm.
+
 #### Installer APIs
 As part of installing or updating an application, the updater will execute the
 application's installer. The API for the application installer is platform-
@@ -239,15 +324,6 @@
 
 Refer to chrome/updater/protos/omaha\_settings.proto for more details.
 
-#### UI
-TODO(crbug.com/1035895): Document UI.
-
-##### Help Button
-If the installation fails, the updater shows an error message with a "Help"
-button. Clicking the help button opens a web page in the user's default browser.
-The page is opened with a query string:
-`?product={AppId}&errorcode={ErrorCode}`.
-
 #### Dynamic Install Parameters
 
 ##### `needsadmin`
@@ -422,13 +498,6 @@
 part of pings to the update server.
 
 ## Updates
-The updater communicates with update servers using the
-[Omaha Protocol](protocol_3_1.md).
-
-### Update Formats
-The updater accepts updates packaged as CRX₃ files. All files must be signed
-with a publisher key. The corresponding public key is hardcoded into the
-updater.
 
 ### Differential Updates
 TODO(crbug.com/1035895): Document differential updates.
diff --git a/extensions/browser/api/system_display/display_info_provider.cc b/extensions/browser/api/system_display/display_info_provider.cc
index 1b979ee..9097b1b 100644
--- a/extensions/browser/api/system_display/display_info_provider.cc
+++ b/extensions/browser/api/system_display/display_info_provider.cc
@@ -37,7 +37,11 @@
 
 }  // namespace
 
-DisplayInfoProvider::DisplayInfoProvider() = default;
+DisplayInfoProvider::DisplayInfoProvider(display::Screen* screen)
+    : screen_(screen ? screen : display::Screen::GetScreen()) {
+  // Do not use/call on the screen object in this constructor yet because a
+  // subclass may pass not-yet-initialized screen instance.
+}
 
 DisplayInfoProvider::~DisplayInfoProvider() = default;
 
@@ -111,9 +115,8 @@
 void DisplayInfoProvider::GetAllDisplaysInfo(
     bool /* single_unified*/,
     base::OnceCallback<void(DisplayUnitInfoList result)> callback) {
-  display::Screen* screen = display::Screen::GetScreen();
-  int64_t primary_id = screen->GetPrimaryDisplay().id();
-  std::vector<display::Display> displays = screen->GetAllDisplays();
+  int64_t primary_id = screen_->GetPrimaryDisplay().id();
+  std::vector<display::Display> displays = screen_->GetAllDisplays();
   DisplayUnitInfoList all_displays;
   for (const display::Display& display : displays) {
     api::system_display::DisplayUnitInfo unit =
diff --git a/extensions/browser/api/system_display/display_info_provider.h b/extensions/browser/api/system_display/display_info_provider.h
index 2d0d26bf..d060ff4 100644
--- a/extensions/browser/api/system_display/display_info_provider.h
+++ b/extensions/browser/api/system_display/display_info_provider.h
@@ -17,6 +17,7 @@
 
 namespace display {
 class Display;
+class Screen;
 }
 
 namespace extensions {
@@ -118,7 +119,7 @@
                              ErrorCallback callback);
 
  protected:
-  DisplayInfoProvider();
+  explicit DisplayInfoProvider(display::Screen* screen = nullptr);
 
   // Trigger OnDisplayChangedEvent
   void DispatchOnDisplayChangedEvent();
@@ -142,6 +143,8 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t metrics) override;
 
+  display::Screen* const screen_;
+
   absl::optional<display::ScopedDisplayObserver> display_observer_;
 };
 
diff --git a/extensions/browser/api/system_display/system_display_apitest.cc b/extensions/browser/api/system_display/system_display_apitest.cc
index 970ee75..b4ccfd1 100644
--- a/extensions/browser/api/system_display/system_display_apitest.cc
+++ b/extensions/browser/api/system_display/system_display_apitest.cc
@@ -13,24 +13,17 @@
 #include "extensions/browser/api/system_display/system_display_api.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/browser/mock_display_info_provider.h"
-#include "extensions/browser/mock_screen.h"
 #include "extensions/common/api/system_display.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/shell/test/shell_apitest.h"
 #include "extensions/test/result_catcher.h"
-#include "ui/display/display.h"
 #include "ui/display/screen.h"
-#include "ui/display/test/scoped_screen_override.h"
 
 namespace extensions {
 
-using display::Screen;
-using display::test::ScopedScreenOverride;
-
 class SystemDisplayApiTest : public ShellApiTest {
  public:
-  SystemDisplayApiTest()
-      : provider_(new MockDisplayInfoProvider), screen_(new MockScreen) {}
+  SystemDisplayApiTest() : provider_(new MockDisplayInfoProvider) {}
 
   SystemDisplayApiTest(const SystemDisplayApiTest&) = delete;
   SystemDisplayApiTest& operator=(const SystemDisplayApiTest&) = delete;
@@ -39,17 +32,9 @@
 
   void SetUpOnMainThread() override {
     ShellApiTest::SetUpOnMainThread();
-    ANNOTATE_LEAKING_OBJECT_PTR(Screen::GetScreen());
-    scoped_screen_override_ =
-        std::make_unique<ScopedScreenOverride>(screen_.get());
     DisplayInfoProvider::InitializeForTesting(provider_.get());
   }
 
-  void TearDownOnMainThread() override {
-    ShellApiTest::TearDownOnMainThread();
-    scoped_screen_override_.reset();
-  }
-
  protected:
   void SetInfo(const std::string& display_id,
                const api::system_display::DisplayProperties& properties) {
@@ -58,8 +43,6 @@
         base::BindOnce([](absl::optional<std::string>) {}));
   }
   std::unique_ptr<MockDisplayInfoProvider> provider_;
-  std::unique_ptr<Screen> screen_;
-  std::unique_ptr<ScopedScreenOverride> scoped_screen_override_;
 };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/extensions/browser/mock_display_info_provider.cc b/extensions/browser/mock_display_info_provider.cc
index 56ae05d..0ce2110 100644
--- a/extensions/browser/mock_display_info_provider.cc
+++ b/extensions/browser/mock_display_info_provider.cc
@@ -16,7 +16,8 @@
 
 namespace extensions {
 
-MockDisplayInfoProvider::MockDisplayInfoProvider() = default;
+MockDisplayInfoProvider::MockDisplayInfoProvider()
+    : DisplayInfoProvider(&screen_) {}
 
 MockDisplayInfoProvider::~MockDisplayInfoProvider() = default;
 
diff --git a/extensions/browser/mock_display_info_provider.h b/extensions/browser/mock_display_info_provider.h
index 4e6fba2..f173f59 100644
--- a/extensions/browser/mock_display_info_provider.h
+++ b/extensions/browser/mock_display_info_provider.h
@@ -13,6 +13,7 @@
 
 #include "base/values.h"
 #include "extensions/browser/api/system_display/display_info_provider.h"
+#include "extensions/browser/mock_screen.h"
 #include "extensions/common/api/system_display.h"
 
 namespace extensions {
@@ -85,6 +86,8 @@
 
   bool native_touch_calibration_success_ = false;
 
+  MockScreen screen_;
+
   api::system_display::MirrorMode mirror_mode_ =
       api::system_display::MIRROR_MODE_OFF;
 };
diff --git a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
index aac03851..204c58e 100644
--- a/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
+++ b/extensions/shell/browser/shell_desktop_controller_aura_unittest.cc
@@ -54,9 +54,7 @@
   ~ShellDesktopControllerAuraTest() override = default;
 
   void SetUp() override {
-    ShellTestBaseAura::SetUp();
-
-    // Set up a screen with 2 displays.
+    // Set up a screen with 2 displays before `ShellTestBaseAura::SetUp()`
     screen_ = std::make_unique<display::ScreenBase>();
     screen_->display_list().AddDisplay(
         display::Display(100, gfx::Rect(0, 0, 1920, 1080)),
@@ -66,6 +64,7 @@
         display::DisplayList::Type::NOT_PRIMARY);
     screen_override_ =
         std::make_unique<display::test::ScopedScreenOverride>(screen_.get());
+    ShellTestBaseAura::SetUp();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     chromeos::PowerManagerClient::InitializeFake();
@@ -80,9 +79,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     chromeos::PowerManagerClient::Shutdown();
 #endif
+    ShellTestBaseAura::TearDown();
     screen_override_.reset();
     screen_.reset();
-    ShellTestBaseAura::TearDown();
   }
 
  protected:
diff --git a/extensions/shell/browser/shell_desktop_controller_mac.h b/extensions/shell/browser/shell_desktop_controller_mac.h
index e78005f2..d3ba91d 100644
--- a/extensions/shell/browser/shell_desktop_controller_mac.h
+++ b/extensions/shell/browser/shell_desktop_controller_mac.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "extensions/shell/browser/desktop_controller.h"
+#include "ui/display/screen.h"
 
 namespace extensions {
 
@@ -36,6 +37,8 @@
   // The desktop only supports a single app window.
   // TODO(yoz): Support multiple app windows, as we do in Aura.
   AppWindow* app_window_;  // NativeAppWindow::Close() deletes this.
+
+  display::ScopedNativeScreen screen_;
 };
 
 }  // namespace extensions
diff --git a/fuchsia/engine/browser/web_engine_browser_main_parts.cc b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
index 38aa87c..1d9425f 100644
--- a/fuchsia/engine/browser/web_engine_browser_main_parts.cc
+++ b/fuchsia/engine/browser/web_engine_browser_main_parts.cc
@@ -55,6 +55,7 @@
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 #include "ui/aura/screen_ozone.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/switches.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/ozone_switches.h"
@@ -167,9 +168,7 @@
     content::ContentBrowserClient* browser_client)
     : browser_client_(browser_client) {}
 
-WebEngineBrowserMainParts::~WebEngineBrowserMainParts() {
-  display::Screen::SetScreenInstance(nullptr);
-}
+WebEngineBrowserMainParts::~WebEngineBrowserMainParts() = default;
 
 std::vector<content::BrowserContext*>
 WebEngineBrowserMainParts::browser_contexts() const {
@@ -190,7 +189,6 @@
 }
 
 int WebEngineBrowserMainParts::PreMainMessageLoopRun() {
-  DCHECK(!screen_);
   DCHECK_EQ(context_bindings_.size(), 0u);
 
   // Initialize the |component_inspector_| to allow diagnostics to be published.
@@ -251,11 +249,7 @@
                           base::Unretained(this)));
 
   // Configure Ozone with an Aura implementation of the Screen abstraction.
-  std::unique_ptr<aura::ScreenOzone> screen_ozone =
-      std::make_unique<aura::ScreenOzone>();
-  screen_ozone.get()->Initialize();
-  screen_ = std::move(screen_ozone);
-  display::Screen::SetScreenInstance(screen_.get());
+  screen_ = std::make_unique<aura::ScopedScreenOzone>();
 
   // Create the FuchsiaCdmManager at startup rather than on-demand, to allow it
   // to perform potentially expensive startup work in the background.
@@ -312,7 +306,6 @@
   // that they may post cleanup tasks during teardown.
   // NOTE: Objects are destroyed in the reverse order of their creation.
   legacy_metrics_client_.reset();
-  screen_.reset();
   intl_profile_watcher_.reset();
 
   base::ImportantFileWriterCleaner::GetInstance().Stop();
diff --git a/fuchsia/engine/browser/web_engine_browser_main_parts.h b/fuchsia/engine/browser/web_engine_browser_main_parts.h
index 91bd530..e878565 100644
--- a/fuchsia/engine/browser/web_engine_browser_main_parts.h
+++ b/fuchsia/engine/browser/web_engine_browser_main_parts.h
@@ -22,7 +22,7 @@
 }
 
 namespace display {
-class Screen;
+class ScopedNativeScreen;
 }
 
 namespace content {
@@ -120,7 +120,7 @@
 
   content::ContentBrowserClient* const browser_client_;
 
-  std::unique_ptr<display::Screen> screen_;
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
 
   // Used to publish diagnostics including the active Contexts and FrameHosts.
   std::unique_ptr<sys::ComponentInspector> component_inspector_;
diff --git a/headless/lib/browser/headless_browser_impl.h b/headless/lib/browser/headless_browser_impl.h
index 7be22a33..7f4ac86 100644
--- a/headless/lib/browser/headless_browser_impl.h
+++ b/headless/lib/browser/headless_browser_impl.h
@@ -14,6 +14,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/task/single_thread_task_runner.h"
+#include "build/build_config.h"
 #include "headless/lib/browser/headless_devtools_manager_delegate.h"
 #include "headless/public/headless_devtools_target.h"
 #include "headless/public/headless_export.h"
@@ -28,6 +29,10 @@
 }  // namespace policy
 #endif
 
+#if BUILDFLAG(IS_MAC)
+#include "ui/display/screen.h"
+#endif
+
 namespace ui {
 class Compositor;
 }  // namespace ui
@@ -117,6 +122,10 @@
 #endif
 
  protected:
+#if BUILDFLAG(IS_MAC)
+  std::unique_ptr<display::ScopedNativeScreen> screen_;
+#endif
+
   base::OnceCallback<void(HeadlessBrowser*)> on_start_callback_;
   HeadlessBrowser::Options options_;
   raw_ptr<HeadlessBrowserMainParts> browser_main_parts_;  // Not owned.
diff --git a/headless/lib/browser/headless_browser_impl_mac.mm b/headless/lib/browser/headless_browser_impl_mac.mm
index 812572a..da3dee43 100644
--- a/headless/lib/browser/headless_browser_impl_mac.mm
+++ b/headless/lib/browser/headless_browser_impl_mac.mm
@@ -65,6 +65,7 @@
 }  // namespace
 
 void HeadlessBrowserImpl::PlatformInitialize() {
+  screen_ = std::make_unique<display::ScopedNativeScreen>();
   HeadlessPopUpMethods::Init();
 }
 
diff --git a/infra/config/generated/builders/ci/android-12-x64-rel/properties.json b/infra/config/generated/builders/ci/android-12-x64-rel/properties.json
index d954e2f..2d5fdd59 100644
--- a/infra/config/generated/builders/ci/android-12-x64-rel/properties.json
+++ b/infra/config/generated/builders/ci/android-12-x64-rel/properties.json
@@ -1,4 +1,56 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-12-x64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x64_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-12-x64-rel",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "android-12-x64-rel",
+          "group": "tryserver.chromium.android"
+        }
+      ]
+    }
+  },
   "$build/reclient": {
     "instance": "rbe-chromium-trusted",
     "jobs": 500,
diff --git a/infra/config/generated/builders/ci/ios14-beta-simulator/properties.json b/infra/config/generated/builders/ci/ios14-beta-simulator/properties.json
deleted file mode 100644
index c18caf9e..0000000
--- a/infra/config/generated/builders/ci/ios14-beta-simulator/properties.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium",
-  "xcode_build_version": "13c100"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios14-sdk-simulator/properties.json b/infra/config/generated/builders/ci/ios14-sdk-simulator/properties.json
deleted file mode 100644
index c18caf9e..0000000
--- a/infra/config/generated/builders/ci/ios14-sdk-simulator/properties.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.fyi",
-  "recipe": "chromium",
-  "xcode_build_version": "13c100"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios16-beta-simulator/properties.json b/infra/config/generated/builders/ci/ios16-beta-simulator/properties.json
new file mode 100644
index 0000000..40047b4
--- /dev/null
+++ b/infra/config/generated/builders/ci/ios16-beta-simulator/properties.json
@@ -0,0 +1,63 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "ios16-beta-simulator",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fyi-archive",
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb",
+                  "mac_toolchain"
+                ],
+                "build_config": "Debug",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "ios"
+              },
+              "legacy_gclient_config": {
+                "config": "ios"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "ios16-beta-simulator",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "ios16-beta-simulator",
+          "group": "tryserver.chromium.mac"
+        }
+      ]
+    }
+  },
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.fyi",
+  "recipe": "chromium",
+  "xcode_build_version": "13c100"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
new file mode 100644
index 0000000..1e7df42
--- /dev/null
+++ b/infra/config/generated/builders/ci/ios16-sdk-simulator/properties.json
@@ -0,0 +1,63 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "ios16-sdk-simulator",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fyi-archive",
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb",
+                  "mac_toolchain"
+                ],
+                "build_config": "Debug",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "ios"
+              },
+              "legacy_gclient_config": {
+                "config": "ios"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "ios16-sdk-simulator",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "ios16-sdk-simulator",
+          "group": "tryserver.chromium.mac"
+        }
+      ]
+    }
+  },
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.fyi",
+  "recipe": "chromium",
+  "xcode_build_version": "13c100"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-12-x64-rel-compilator/properties.json b/infra/config/generated/builders/try/android-12-x64-rel-compilator/properties.json
index dc7af2f..011e6b4 100644
--- a/infra/config/generated/builders/try/android-12-x64-rel-compilator/properties.json
+++ b/infra/config/generated/builders/try/android-12-x64-rel-compilator/properties.json
@@ -1,4 +1,50 @@
 {
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-12-x64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x64_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-12-x64-rel",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
   "$build/goma": {
     "enable_ats": true,
     "jobs": 300,
diff --git a/infra/config/generated/builders/try/android-12-x64-rel/properties.json b/infra/config/generated/builders/try/android-12-x64-rel/properties.json
index 4205c5c..b04e2972 100644
--- a/infra/config/generated/builders/try/android-12-x64-rel/properties.json
+++ b/infra/config/generated/builders/try/android-12-x64-rel/properties.json
@@ -3,6 +3,52 @@
     "compilator": "android-12-x64-rel-compilator",
     "compilator_watcher_git_revision": "7809a690bbd935bcb3b4d922e24cabe168aaabc8"
   },
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "android-12-x64-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-android-archive",
+              "builder_group": "chromium.android",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "x64_builder"
+              },
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "android",
+                "target_bits": 64,
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android",
+                  "enable_reclient"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "android-12-x64-rel",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
diff --git a/infra/config/generated/builders/try/ios14-beta-simulator/properties.json b/infra/config/generated/builders/try/ios14-beta-simulator/properties.json
deleted file mode 100644
index b9041d6..0000000
--- a/infra/config/generated/builders/try/ios14-beta-simulator/properties.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.mac",
-  "recipe": "chromium_trybot",
-  "xcode_build_version": "13c100"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios14-sdk-simulator/properties.json b/infra/config/generated/builders/try/ios14-sdk-simulator/properties.json
deleted file mode 100644
index b9041d6..0000000
--- a/infra/config/generated/builders/try/ios14-sdk-simulator/properties.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "$build/goma": {
-    "rpc_extra_params": "?prod",
-    "server_host": "goma.chromium.org",
-    "use_luci_auth": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.mac",
-  "recipe": "chromium_trybot",
-  "xcode_build_version": "13c100"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios16-beta-simulator/properties.json b/infra/config/generated/builders/try/ios16-beta-simulator/properties.json
new file mode 100644
index 0000000..d39f3de
--- /dev/null
+++ b/infra/config/generated/builders/try/ios16-beta-simulator/properties.json
@@ -0,0 +1,57 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "ios16-beta-simulator",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fyi-archive",
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb",
+                  "mac_toolchain"
+                ],
+                "build_config": "Debug",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "ios"
+              },
+              "legacy_gclient_config": {
+                "config": "ios"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "ios16-beta-simulator",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.mac",
+  "recipe": "chromium_trybot",
+  "xcode_build_version": "13c100"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
new file mode 100644
index 0000000..25ca46bb
--- /dev/null
+++ b/infra/config/generated/builders/try/ios16-sdk-simulator/properties.json
@@ -0,0 +1,57 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "ios16-sdk-simulator",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fyi-archive",
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb",
+                  "mac_toolchain"
+                ],
+                "build_config": "Debug",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "ios"
+              },
+              "legacy_gclient_config": {
+                "config": "ios"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "ios16-sdk-simulator",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.mac",
+  "recipe": "chromium_trybot",
+  "xcode_build_version": "13c100"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index f032209..0f11399 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1150,14 +1150,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/ios14-beta-simulator"
-        includable_only: true
-      }
-      builders {
-        name: "chromium/try/ios14-sdk-simulator"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/ios15-beta-simulator"
         includable_only: true
       }
@@ -1166,6 +1158,14 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/ios16-beta-simulator"
+        includable_only: true
+      }
+      builders {
+        name: "chromium/try/ios16-sdk-simulator"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/lacros-amd64-generic-rel"
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 574d45c..ede95d6 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -30573,168 +30573,6 @@
       }
     }
     builders {
-      name: "ios14-beta-simulator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios14-beta-simulator"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-11"
-      dimensions: "pool:luci.chromium.ci"
-      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/ios14-beta-simulator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 36000
-      caches {
-        name: "xcode_ios_13c100"
-        path: "xcode_ios_13c100.app"
-      }
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        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: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: "ios14-sdk-simulator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios14-sdk-simulator"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-11"
-      dimensions: "pool:luci.chromium.ci"
-      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/ios14-sdk-simulator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 36000
-      caches {
-        name: "xcode_ios_13c100"
-        path: "xcode_ios_13c100.app"
-      }
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        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: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: "ios15-beta-simulator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:ios15-beta-simulator"
@@ -30978,6 +30816,168 @@
       }
     }
     builders {
+      name: "ios16-beta-simulator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:ios16-beta-simulator"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Mac-11"
+      dimensions: "pool:luci.chromium.ci"
+      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/ios16-beta-simulator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 36000
+      caches {
+        name: "xcode_ios_13c100"
+        path: "xcode_ios_13c100.app"
+      }
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        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: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: "ios16-sdk-simulator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:ios16-sdk-simulator"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Mac-11"
+      dimensions: "pool:luci.chromium.ci"
+      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/ios16-sdk-simulator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 36000
+      caches {
+        name: "xcode_ios_13c100"
+        path: "xcode_ios_13c100.app"
+      }
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        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: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: "lacros-amd64-generic-binary-size-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -61141,190 +61141,6 @@
       }
     }
     builders {
-      name: "ios14-beta-simulator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios14-beta-simulator"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-11"
-      dimensions: "pool:luci.chromium.try"
-      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/try/ios14-beta-simulator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.mac",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      caches {
-        name: "xcode_ios_13c100"
-        path: "xcode_ios_13c100.app"
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
-      name: "ios14-sdk-simulator"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:ios14-sdk-simulator"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-11"
-      dimensions: "pool:luci.chromium.try"
-      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/try/ios14-sdk-simulator/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.mac",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "win_toolchain"
-        path: "win_toolchain"
-      }
-      caches {
-        name: "xcode_ios_13c100"
-        path: "xcode_ios_13c100.app"
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "ios15-beta-simulator"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:ios15-beta-simulator"
@@ -61509,6 +61325,190 @@
       }
     }
     builders {
+      name: "ios16-beta-simulator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:ios16-beta-simulator"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Mac-11"
+      dimensions: "pool:luci.chromium.try"
+      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/try/ios16-beta-simulator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.mac",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      caches {
+        name: "xcode_ios_13c100"
+        path: "xcode_ios_13c100.app"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
+      name: "ios16-sdk-simulator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:ios16-sdk-simulator"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Mac-11"
+      dimensions: "pool:luci.chromium.try"
+      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/try/ios16-sdk-simulator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.mac",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      caches {
+        name: "xcode_ios_13c100"
+        path: "xcode_ios_13c100.app"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "lacros-amd64-generic-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:lacros-amd64-generic-rel"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index f3c1d73..238d045 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -7707,16 +7707,6 @@
     short_name: "wk"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/ios14-beta-simulator"
-    category: "iOS|iOS14"
-    short_name: "ios14"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ios14-sdk-simulator"
-    category: "iOS|iOS14"
-    short_name: "sdk14"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/ios15-sdk-device"
     category: "iOS|iOS15"
     short_name: "dev"
@@ -7732,6 +7722,16 @@
     short_name: "sdk15"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/ios16-beta-simulator"
+    category: "iOS|iOS16"
+    short_name: "ios16"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/ios16-sdk-simulator"
+    category: "iOS|iOS16"
+    short_name: "sdk16"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/ios-m1-simulator"
     category: "iOS|iOSM1"
     short_name: "iosM1"
@@ -16043,18 +16043,18 @@
     name: "buildbucket/luci.chromium.try/ios-simulator-rts"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/ios14-beta-simulator"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios14-sdk-simulator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/ios15-beta-simulator"
   }
   builders {
     name: "buildbucket/luci.chromium.try/ios15-sdk-simulator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/ios16-beta-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios16-sdk-simulator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/lacros-amd64-generic-rel"
   }
   builders {
@@ -17425,18 +17425,18 @@
     name: "buildbucket/luci.chromium.try/ios-simulator-rts"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/ios14-beta-simulator"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/ios14-sdk-simulator"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/ios15-beta-simulator"
   }
   builders {
     name: "buildbucket/luci.chromium.try/ios15-sdk-simulator"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/ios16-beta-simulator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/ios16-sdk-simulator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/mac-arm64-on-arm64-rel"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index b41d55a..dfbb3a8 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5169,28 +5169,6 @@
   }
 }
 job {
-  id: "ios14-beta-simulator"
-  realm: "ci"
-  schedule: "0 0,4,8,12,16,20 * * *"
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "ios14-beta-simulator"
-  }
-}
-job {
-  id: "ios14-sdk-simulator"
-  realm: "ci"
-  schedule: "0 2,6,10,14,18,22 * * *"
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "ios14-sdk-simulator"
-  }
-}
-job {
   id: "ios15-beta-simulator"
   realm: "ci"
   acl_sets: "ci"
@@ -5221,6 +5199,28 @@
   }
 }
 job {
+  id: "ios16-beta-simulator"
+  realm: "ci"
+  schedule: "0 0,4,8,12,16,20 * * *"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "ios16-beta-simulator"
+  }
+}
+job {
+  id: "ios16-sdk-simulator"
+  realm: "ci"
+  schedule: "0 2,6,10,14,18,22 * * *"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "ios16-sdk-simulator"
+  }
+}
+job {
   id: "lacros-amd64-generic-binary-size-rel"
   realm: "ci"
   acl_sets: "ci"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
index f2864ac..71e1d142 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -1152,6 +1152,28 @@
 
 ci.builder(
     name = "android-12-x64-rel",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "android",
+                "enable_reclient",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "android",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.ANDROID,
+        ),
+        android_config = builder_config.android_config(
+            config = "x64_builder",
+        ),
+        build_gs_bucket = "chromium-android-archive",
+    ),
     console_view_entry = consoles.console_view_entry(
         category = "builder_tester|x64",
         short_name = "12",
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 68dc731..5f1213c 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1330,28 +1330,6 @@
 )
 
 fyi_ios_builder(
-    name = "ios14-beta-simulator",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|iOS14",
-        short_name = "ios14",
-    ),
-    os = os.MAC_11,
-    schedule = "0 0,4,8,12,16,20 * * *",
-    triggered_by = [],
-)
-
-fyi_ios_builder(
-    name = "ios14-sdk-simulator",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|iOS14",
-        short_name = "sdk14",
-    ),
-    os = os.MAC_11,
-    schedule = "0 2,6,10,14,18,22 * * *",
-    triggered_by = [],
-)
-
-fyi_ios_builder(
     name = "ios15-beta-simulator",
     console_view_entry = [
         consoles.console_view_entry(
@@ -1386,6 +1364,60 @@
     xcode = xcode.x13betabots,
 )
 
+fyi_ios_builder(
+    name = "ios16-beta-simulator",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "ios",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+                "mac_toolchain",
+            ],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.IOS,
+        ),
+        build_gs_bucket = "chromium-fyi-archive",
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|iOS16",
+        short_name = "ios16",
+    ),
+    os = os.MAC_11,
+    schedule = "0 0,4,8,12,16,20 * * *",
+    triggered_by = [],
+)
+
+fyi_ios_builder(
+    name = "ios16-sdk-simulator",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "ios",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+                "mac_toolchain",
+            ],
+            build_config = builder_config.build_config.DEBUG,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.IOS,
+        ),
+        build_gs_bucket = "chromium-fyi-archive",
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|iOS16",
+        short_name = "sdk16",
+    ),
+    os = os.MAC_11,
+    schedule = "0 2,6,10,14,18,22 * * *",
+    triggered_by = [],
+)
+
 ci.builder(
     # An FYI version of the following builders that runs on Focal:
     # https://ci.chromium.org/p/chromium/builders/ci/Linux%20MSan%20Builder
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index c88469d..0be076f9 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -48,6 +48,9 @@
 try_.orchestrator_builder(
     name = "android-12-x64-rel",
     compilator = "android-12-x64-rel-compilator",
+    mirrors = [
+        "ci/android-12-x64-rel",
+    ],
     # TODO(crbug.com/1225851): Enable it on branch after running on CQ
     # branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index c79a1b8..abeb5a0 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -374,16 +374,6 @@
 )
 
 ios_builder(
-    name = "ios14-beta-simulator",
-    os = os.MAC_11,
-)
-
-ios_builder(
-    name = "ios14-sdk-simulator",
-    os = os.MAC_11,
-)
-
-ios_builder(
     name = "ios15-beta-simulator",
 )
 
@@ -393,6 +383,22 @@
     os = os.MAC_12,
 )
 
+ios_builder(
+    name = "ios16-beta-simulator",
+    os = os.MAC_11,
+    mirrors = [
+        "ci/ios16-beta-simulator",
+    ],
+)
+
+ios_builder(
+    name = "ios16-sdk-simulator",
+    os = os.MAC_11,
+    mirrors = [
+        "ci/ios16-sdk-simulator",
+    ],
+)
+
 try_.gpu.optional_tests_builder(
     name = "mac_optional_gpu_tests_rel",
     branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
diff --git a/ios/build/bots/scripts/test_apps.py b/ios/build/bots/scripts/test_apps.py
index 355d322..9752c0d 100644
--- a/ios/build/bots/scripts/test_apps.py
+++ b/ios/build/bots/scripts/test_apps.py
@@ -131,6 +131,11 @@
     self.host_app_path = kwargs.get('host_app_path')
     self.inserted_libs = kwargs.get('inserted_libs') or []
 
+  def remove_gtest_sharding_env_vars(self):
+    """Removes sharding related env vars from self.env_vars."""
+    for env_var_key in ['GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS']:
+      self.env_vars.pop(env_var_key, None)
+
   def fill_xctest_run(self, out_dir):
     """Fills xctestrun file by egtests.
 
diff --git a/ios/build/bots/scripts/test_apps_test.py b/ios/build/bots/scripts/test_apps_test.py
index 40a4d8f9e..77a6505a 100755
--- a/ios/build/bots/scripts/test_apps_test.py
+++ b/ios/build/bots/scripts/test_apps_test.py
@@ -228,6 +228,27 @@
                               '_module']['CommandLineArguments']
     self.assertTrue('--gtest_repeat=2' in cmd_args)
 
+  @mock.patch('test_apps.get_bundle_id', return_value=_BUNDLE_ID)
+  @mock.patch('os.path.exists', return_value=True)
+  def test_remove_gtest_sharding_env_vars(self, _1, _2):
+    gtests_app = test_apps.GTestsApp(
+        'app_path', env_vars=['GTEST_SHARD_INDEX=1', 'GTEST_TOTAL_SHARDS=2'])
+    assert all(key in gtests_app.env_vars
+               for key in ['GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'])
+    gtests_app.remove_gtest_sharding_env_vars()
+    assert not any(key in gtests_app.env_vars
+                   for key in ['GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'])
+
+  @mock.patch('test_apps.get_bundle_id', return_value=_BUNDLE_ID)
+  @mock.patch('os.path.exists', return_value=True)
+  def test_remove_gtest_sharding_env_vars_non_exist(self, _1, _2):
+    gtests_app = test_apps.GTestsApp('app_path')
+    assert not any(key in gtests_app.env_vars
+                   for key in ['GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'])
+    gtests_app.remove_gtest_sharding_env_vars()
+    assert not any(key in gtests_app.env_vars
+                   for key in ['GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'])
+
 
 class EgtestsAppTest(test_runner_test.TestCase):
   """Tests to test methods of EgTestsApp."""
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 54802e22..69f382d 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -596,6 +596,11 @@
           LOGGER.warning('Crashed during %s, resuming...\n',
                          list(result.crashed_tests()))
           test_app.excluded_tests = list(overall_result.all_test_names())
+          # Changing test filter will change selected gtests in this shard.
+          # Thus, sharding env vars have to be cleared to ensure needed tests
+          # are run. This means there might be duplicate same tests across
+          # the shards.
+          test_app.remove_gtest_sharding_env_vars()
           retry_out_dir = os.path.join(
               self.out_dir, 'retry_after_crash_%d' % int(time.time()))
           result = self._run(
@@ -622,6 +627,10 @@
           for test in tests_to_retry:
             LOGGER.info('Retry #%s for %s.\n', i + 1, test)
             test_app.included_tests = [test]
+            # Changing test filter will change selected gtests in this shard.
+            # Thus, sharding env vars have to be cleared to ensure the test
+            # runs when it's the only test in gtest_filter.
+            test_app.remove_gtest_sharding_env_vars()
             test_retry_sub_dir = '%s_retry_%d' % (test.replace('/', '_'), i)
             retry_out_dir = os.path.join(self.out_dir, test_retry_sub_dir)
             retry_result = self._run(
diff --git a/ios/chrome/browser/BUILD.gn b/ios/chrome/browser/BUILD.gn
index 2d80583..e69dcd5 100644
--- a/ios/chrome/browser/BUILD.gn
+++ b/ios/chrome/browser/BUILD.gn
@@ -265,6 +265,7 @@
     "//rlz/buildflags",
     "//services/network:network_service",
     "//ui/base",
+    "//ui/display",
   ]
 
   if (enable_rlz) {
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index c43c354..733a7d65 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -85,6 +85,10 @@
 #include "base/allocator/allocator_shim.h"
 #endif
 
+#if DCHECK_IS_ON()
+#include "ui/display/screen_base.h"
+#endif
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -125,7 +129,15 @@
   net::URLRequest::SetDefaultCookiePolicyToBlock();
 }
 
-IOSChromeMainParts::~IOSChromeMainParts() {}
+IOSChromeMainParts::~IOSChromeMainParts() {
+#if DCHECK_IS_ON()
+  // The screen object is never deleted on IOS. Make sure that all display
+  // observers are removed at the end.
+  display::ScreenBase* screen =
+      static_cast<display::ScreenBase*>(display::Screen::GetScreen());
+  DCHECK(!screen->HasDisplayObservers());
+#endif
+}
 
 void IOSChromeMainParts::PreEarlyInitialization() {
 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 1516de0..98aff0c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4fc130573215e902c5e556c32ab73d3c3c2bcb69
\ No newline at end of file
+da2bc8b39be83996a338bb4533e039086c8f3d5f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index e80387b..110b71f 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-d7348369bb2fecda767b13d0ca52c826c171037a
\ No newline at end of file
+f9680a08f5d364609cc7088c952d424e2223d15e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index d22f654..782f0403 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4131c1b7045b3a92a99ad185d8a59ebed63ca312
\ No newline at end of file
+189045ce9b6ab6e0de583c3f3f6f1bd33d3c666f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 67ce3a2..088eb003 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-cc79400827a328fa806ac182b630e97f0fe57136
\ No newline at end of file
+fbd2678c6e85d3d9bc3c052f8cee4a48aed0de5e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index c6c59cb..f3891da5 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-9d8f9a4af3f683ae32be49858c4a521b9415b86c
\ No newline at end of file
+bcd9e458d866505d51b404592dea764e5c2c5c87
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index c3adbfeef..235e1f34 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7ca2d0b55fbaf6554beedc7ebdd658e96671602c
\ No newline at end of file
+821ab244e45baa91893f2776299d31a67f615bd1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 2b9511f..f29c684 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0668c5faa00ce98b0c1b2c042306d9789fcd02f0
\ No newline at end of file
+11aa14dc070b66c701f5018d46a77b5e6cec4770
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 4fc19228..2426973 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5b70c584e649ed18839a48020c655d907e5e65f3
\ No newline at end of file
+c3c3d96709d8be611c3d4c59ea8fdd9dbae589b7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 35fdba5d..d597f911 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4fe41dd5965d0790dcb833866198f3c9ec00cf39
\ No newline at end of file
+2b1890856f2febe5baf4f1e867f07fc62300b9d4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 16e1f69..0ec5d9e4 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-bec973c5da4b0f03a6b496ad263e0d99c4e572fc
\ No newline at end of file
+24b33584539f5882723b77d6722db91e06bcec09
\ No newline at end of file
diff --git a/ios/testing/earl_grey/BUILD.gn b/ios/testing/earl_grey/BUILD.gn
index 6f5935bb..a5b4ab0 100644
--- a/ios/testing/earl_grey/BUILD.gn
+++ b/ios/testing/earl_grey/BUILD.gn
@@ -63,5 +63,6 @@
     "//ios/third_party/edo",
     "//ios/web/common",
     "//testing/gtest:gtest",
+    "//ui/display",
   ]
 }
diff --git a/ios/testing/earl_grey/DEPS b/ios/testing/earl_grey/DEPS
new file mode 100644
index 0000000..e5069168
--- /dev/null
+++ b/ios/testing/earl_grey/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "base_earl_grey_test_case.mm": [
+    "+ui/display"
+  ]
+}
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index da86c76c..5440688 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -16,6 +16,10 @@
 #import "ios/testing/earl_grey/coverage_utils.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
+#if DCHECK_IS_ON()
+#include "ui/display/screen_base.h"
+#endif
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -72,6 +76,14 @@
 }
 
 + (void)tearDown {
+#if DCHECK_IS_ON()
+  // The same screen object is shared across multiple test runs on IOS build.
+  // Make sure that all display observers are removed at the end of each
+  // test.
+  display::ScreenBase* screen =
+      static_cast<display::ScreenBase*>(display::Screen::GetScreen());
+  DCHECK(!screen->HasDisplayObservers());
+#endif
   if ([[AppLaunchManager sharedManager] appIsLaunched]) {
     [CoverageUtils writeClangCoverageProfile];
     [CoverageUtils resetCoverageProfileCounters];
diff --git a/ios/web/shell/BUILD.gn b/ios/web/shell/BUILD.gn
index bdf5d2c9..a35ad69 100644
--- a/ios/web/shell/BUILD.gn
+++ b/ios/web/shell/BUILD.gn
@@ -63,6 +63,7 @@
     "//net",
     "//net:extras",
     "//ui/base",
+    "//ui/display",
   ]
 
   frameworks = [
diff --git a/ios/web/shell/shell_web_main_parts.mm b/ios/web/shell/shell_web_main_parts.mm
index 9833401f..0e9bbbf 100644
--- a/ios/web/shell/shell_web_main_parts.mm
+++ b/ios/web/shell/shell_web_main_parts.mm
@@ -6,6 +6,10 @@
 
 #include "ios/web/shell/shell_browser_state.h"
 
+#if DCHECK_IS_ON()
+#include "ui/display/screen_base.h"
+#endif
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -16,6 +20,13 @@
 }
 
 ShellWebMainParts::~ShellWebMainParts() {
+#if DCHECK_IS_ON()
+  // The screen object is never deleted on IOS. Make sure that all display
+  // observers are removed at the end.
+  display::ScreenBase* screen =
+      static_cast<display::ScreenBase*>(display::Screen::GetScreen());
+  DCHECK(!screen->HasDisplayObservers());
+#endif
 }
 
 void ShellWebMainParts::PreMainMessageLoopRun() {
diff --git a/ios/web/test/BUILD.gn b/ios/web/test/BUILD.gn
index 60324ccc8..fe065aa6 100644
--- a/ios/web/test/BUILD.gn
+++ b/ios/web/test/BUILD.gn
@@ -71,6 +71,7 @@
     "//net",
     "//third_party/ocmock",
     "//ui/base",
+    "//ui/display",
   ]
 
   sources = [
diff --git a/ios/web/test/web_int_test.mm b/ios/web/test/web_int_test.mm
index ad4cca2..896a5add 100644
--- a/ios/web/test/web_int_test.mm
+++ b/ios/web/test/web_int_test.mm
@@ -15,6 +15,10 @@
 #import "ios/web/public/test/web_view_interaction_test_util.h"
 #include "ios/web/public/web_state_observer.h"
 
+#if DCHECK_IS_ON()
+#include "ui/display/screen_base.h"
+#endif
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -81,6 +85,15 @@
                              [WKWebsiteDataStore allWebsiteDataTypes]);
 
   WebTest::TearDown();
+
+#if DCHECK_IS_ON()
+  // The same screen object is shared across multiple test runs on IOS build.
+  // Make sure that all display observers are removed at the end of each
+  // test.
+  display::ScreenBase* screen =
+      static_cast<display::ScreenBase*>(display::Screen::GetScreen());
+  DCHECK(!screen->HasDisplayObservers());
+#endif
 }
 
 bool WebIntTest::ExecuteBlockAndWaitForLoad(const GURL& url,
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 55a16746..3a9fd83 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -371,6 +371,7 @@
     "//services/network:network_service",
     "//third_party/abseil-cpp:absl",
     "//ui/base",
+    "//ui/display",
     "//url",
   ]
 
diff --git a/ios/web_view/internal/DEPS b/ios/web_view/internal/DEPS
index 3d26b42b..271ac1bb 100644
--- a/ios/web_view/internal/DEPS
+++ b/ios/web_view/internal/DEPS
@@ -69,3 +69,9 @@
   "+ui/base",
   "+ui/gfx",
 ]
+
+specific_include_rules = {
+  "web_view_web_main_parts.mm": [
+    "+ui/display"
+  ]
+}
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index f6a80877..534632d7 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -24,6 +24,10 @@
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/base/resource/resource_bundle.h"
 
+#if DCHECK_IS_ON()
+#include "ui/display/screen_base.h"
+#endif
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -33,7 +37,15 @@
 WebViewWebMainParts::WebViewWebMainParts()
     : field_trial_list_(/*entropy_provider=*/nullptr) {}
 
-WebViewWebMainParts::~WebViewWebMainParts() = default;
+WebViewWebMainParts::~WebViewWebMainParts() {
+#if DCHECK_IS_ON()
+  // The screen object is never deleted on IOS. Make sure that all display
+  // observers are removed at the end.
+  display::ScreenBase* screen =
+      static_cast<display::ScreenBase*>(display::Screen::GetScreen());
+  DCHECK(!screen->HasDisplayObservers());
+#endif
+}
 
 void WebViewWebMainParts::PreCreateMainMessageLoop() {
   l10n_util::OverrideLocaleWithCocoaLocale();
diff --git a/media/base/callback_registry.h b/media/base/callback_registry.h
index b89be2c..faf5504 100644
--- a/media/base/callback_registry.h
+++ b/media/base/callback_registry.h
@@ -70,8 +70,8 @@
   void Notify(Args&&... args) {
     DVLOG(1) << __func__;
     base::AutoLock lock(lock_);
-    for (auto const& entry : callbacks_)
-      entry.second.Run(std::forward<Args>(args)...);
+    for (auto const& [key_id, callback] : callbacks_)
+      callback.Run(std::forward<Args>(args)...);
   }
 
  private:
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 31f3dbe..7ae312d8 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -108,6 +108,7 @@
                void(const std::vector<MediaTrack::Id>&, base::OnceClosure));
   MOCK_METHOD2(OnSelectedVideoTrackChanged,
                void(absl::optional<MediaTrack::Id>, base::OnceClosure));
+  MOCK_METHOD0(OnExternalVideoFrameRequest, void());
 
   // TODO(sandersd): This should automatically return true between Start() and
   // Stop(). (Or better, remove it from the interface entirely.)
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index 556d62c..f1c6ac1 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -151,6 +151,10 @@
       absl::optional<MediaTrack::Id> selected_track_id,
       base::OnceClosure change_completed_cb) = 0;
 
+  // Signal to the pipeline that there has been a client request to access
+  // video frame data.
+  virtual void OnExternalVideoFrameRequest() = 0;
+
   // Stops the pipeline. This is a blocking function.
   // If the pipeline is started, it must be stopped before destroying it.
   // It it permissible to call Stop() at any point during the lifetime of the
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 93f4e72..23f06c44 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -100,6 +100,8 @@
       absl::optional<MediaTrack::Id> selected_track_id,
       base::OnceClosure change_completed_cb);
 
+  void OnExternalVideoFrameRequest();
+
  private:
   // Contains state shared between main and media thread. On the media thread
   // each member can be read without locking, but writing requires locking. On
@@ -756,6 +758,27 @@
                      std::move(change_completed_cb)));
 }
 
+void PipelineImpl::OnExternalVideoFrameRequest() {
+  // This function is currently a no-op unless we're on a Windows build with
+  // Media Foundation for Clear running.
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!external_video_frame_request_signaled_) {
+    external_video_frame_request_signaled_ = true;
+    media_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&RendererWrapper::OnExternalVideoFrameRequest,
+                                  base::Unretained(renderer_wrapper_.get())));
+  }
+}
+
+void PipelineImpl::RendererWrapper::OnExternalVideoFrameRequest() {
+  DCHECK(media_task_runner_->BelongsToCurrentThread());
+  if (!shared_state_.renderer) {
+    return;
+  }
+
+  shared_state_.renderer->OnExternalVideoFrameRequest();
+}
+
 void PipelineImpl::RendererWrapper::OnDemuxerCompletedTrackChange(
     base::OnceClosure change_completed_cb,
     DemuxerStream::Type stream_type,
@@ -1242,6 +1265,7 @@
   seek_cb_ = std::move(seek_cb);
   last_media_time_ = base::TimeDelta();
   seek_time_ = kNoTimestamp;
+  external_video_frame_request_signaled_ = false;
 
   // By default, create a default renderer to avoid additional start-to-play
   // latency caused by asynchronous Renderer creation. When |start_type| is
@@ -1335,6 +1359,7 @@
   seek_cb_ = std::move(seek_cb);
   seek_time_ = time;
   last_media_time_ = base::TimeDelta();
+  external_video_frame_request_signaled_ = false;
 
   // Always create a default renderer for Resume().
   auto default_renderer = create_renderer_cb_.Run(absl::nullopt);
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 6262fa9..0a930d0 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -130,6 +130,8 @@
       absl::optional<MediaTrack::Id> selected_track_id,
       base::OnceClosure change_completed_cb) override;
 
+  void OnExternalVideoFrameRequest() override;
+
  private:
   friend class MediaLog;
   class RendererWrapper;
@@ -219,6 +221,13 @@
   // Cached suspension state for the RendererWrapper.
   bool is_suspended_;
 
+  // 'external_video_frame_request_signaled_' tracks whether we've called
+  // OnExternalVideoFrameRequest on the current renderer. Calls which may
+  // create a new renderer in the RendererWrapper (Start, Resume, SetCdm) will
+  // reset this member. There is no guarantee to the client that
+  // OnExternalVideoFrameRequest will be called only once.
+  bool external_video_frame_request_signaled_ = false;
+
   base::ThreadChecker thread_checker_;
   base::WeakPtrFactory<PipelineImpl> weak_factory_{this};
 };
diff --git a/media/base/renderer.cc b/media/base/renderer.cc
index c83cf95e..e2cb8d3 100644
--- a/media/base/renderer.cc
+++ b/media/base/renderer.cc
@@ -39,4 +39,8 @@
   // Not supported by most renderers.
 }
 
+void Renderer::OnExternalVideoFrameRequest() {
+  // Default implementation of OnExternalVideoFrameRequest is to no-op.
+}
+
 }  // namespace media
diff --git a/media/base/renderer.h b/media/base/renderer.h
index 8b26b864..b7738604f 100644
--- a/media/base/renderer.h
+++ b/media/base/renderer.h
@@ -92,6 +92,17 @@
   virtual void OnEnabledAudioTracksChanged(
       const std::vector<DemuxerStream*>& enabled_tracks,
       base::OnceClosure change_completed_cb);
+
+  // Signal to the renderer that there has been a client request to access a
+  // VideoFrame. This signal may be used by the renderer to ensure it is
+  // operating in a mode which produces a VideoFrame usable by the client.
+  // E.g., the MediaFoundationRendererClient on Windows has two modes
+  // of operation: Frame Server & Direct Composition. Direct Composition mode
+  // does not produce a VideoFrame with an accessible 'data' buffer, so clients
+  // cannot access the underlying image data. In order for
+  // MediaFoundationRendererClient to produce a VideoFrame with 'data'
+  // accessible by the client it must switch to operate in Frame Server mode.
+  virtual void OnExternalVideoFrameRequest();
 };
 
 }  // namespace media
diff --git a/media/capabilities/webrtc_video_stats_db_impl.cc b/media/capabilities/webrtc_video_stats_db_impl.cc
index fefbc7b7..dd524f5 100644
--- a/media/capabilities/webrtc_video_stats_db_impl.cc
+++ b/media/capabilities/webrtc_video_stats_db_impl.cc
@@ -413,10 +413,10 @@
     collection.emplace();
     const base::TimeDelta max_time_to_keep_stats = GetMaxTimeToKeepStats();
 
-    for (auto const& stats_proto : *stats_proto_collection) {
-      if (AreStatsValid(&stats_proto.second)) {
+    for (auto const& [pixel_key, video_stats_entry] : *stats_proto_collection) {
+      if (AreStatsValid(&video_stats_entry)) {
         VideoStatsEntry entry;
-        for (auto const& stats : stats_proto.second.stats()) {
+        for (auto const& stats : video_stats_entry.stats()) {
           if (wall_clock_->Now() - base::Time::FromJsTime(stats.timestamp()) <=
               max_time_to_keep_stats) {
             entry.emplace_back(stats.timestamp(), stats.frames_processed(),
@@ -427,7 +427,7 @@
 
         if (!entry.empty()) {
           absl::optional<int> pixels =
-              VideoDescKey::ParsePixelsFromKey(stats_proto.first);
+              VideoDescKey::ParsePixelsFromKey(pixel_key);
           if (pixels) {
             collection->insert({*pixels, std::move(entry)});
           }
diff --git a/media/capture/video/linux/fake_v4l2_impl.cc b/media/capture/video/linux/fake_v4l2_impl.cc
index 40b22bb..94e9af7 100644
--- a/media/capture/video/linux/fake_v4l2_impl.cc
+++ b/media/capture/video/linux/fake_v4l2_impl.cc
@@ -80,6 +80,8 @@
     timeperframe_.denominator = kDefaultFrameInternvalDenominator;
   }
 
+  ~OpenedDevice() { DCHECK(!frame_production_thread_.IsRunning()); }
+
   const std::string& device_id() const { return config_.descriptor.device_id; }
 
   int open_flags() const { return open_flags_; }
diff --git a/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc b/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc
index 8fe2587..38b9519 100644
--- a/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc
+++ b/media/capture/video/linux/video_capture_device_factory_linux_unittest.cc
@@ -35,6 +35,8 @@
                                            std::move(fake_device_provider));
   }
 
+  void TearDown() override { task_environment_.RunUntilIdle(); }
+
   base::test::TaskEnvironment task_environment_;
   FakeV4L2Impl* fake_v4l2_;
   FakeDeviceProvider* fake_device_provider_;
diff --git a/media/capture/video/linux/video_capture_device_linux.cc b/media/capture/video/linux/video_capture_device_linux.cc
index b4dfd14..3f2848a 100644
--- a/media/capture/video/linux/video_capture_device_linux.cc
+++ b/media/capture/video/linux/video_capture_device_linux.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "media/capture/video/linux/v4l2_capture_delegate.h"
 
@@ -56,15 +57,16 @@
     const VideoCaptureDeviceDescriptor& device_descriptor)
     : device_descriptor_(device_descriptor),
       v4l2_(std::move(v4l2)),
-      v4l2_thread_("V4L2CaptureThread"),
+      task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
+          {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
+           base::WithBaseSyncPrimitives()},
+          base::SingleThreadTaskRunnerThreadMode::DEDICATED)),
       rotation_(0) {}
 
 VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Check if the thread is running.
-  // This means that the device has not been StopAndDeAllocate()d properly.
-  DCHECK(!v4l2_thread_.IsRunning());
-  v4l2_thread_.Stop();
+  DCHECK(!capture_impl_)
+      << "StopAndDeAllocate() must be called before destruction.";
 }
 
 void VideoCaptureDeviceLinux::AllocateAndStart(
@@ -72,97 +74,74 @@
     std::unique_ptr<VideoCaptureDevice::Client> client) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!capture_impl_);
-  if (v4l2_thread_.IsRunning())
-    return;  // Wrong state.
-  v4l2_thread_.Start();
 
   const int line_frequency =
       TranslatePowerLineFrequencyToV4L2(GetPowerLineFrequency(params));
   capture_impl_ = std::make_unique<V4L2CaptureDelegate>(
-      v4l2_.get(), device_descriptor_, v4l2_thread_.task_runner(),
-      line_frequency, rotation_);
+      v4l2_.get(), device_descriptor_, task_runner_, line_frequency, rotation_);
   if (!capture_impl_) {
     client->OnError(VideoCaptureError::
                         kDeviceCaptureLinuxFailedToCreateVideoCaptureDelegate,
                     FROM_HERE, "Failed to create VideoCaptureDelegate");
     return;
   }
-  v4l2_thread_.task_runner()->PostTask(
+  task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&V4L2CaptureDelegate::AllocateAndStart,
                      capture_impl_->GetWeakPtr(),
                      params.requested_format.frame_size.width(),
                      params.requested_format.frame_size.height(),
                      params.requested_format.frame_rate, std::move(client)));
-
-  for (auto& request : photo_requests_queue_)
-    v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(request));
-  photo_requests_queue_.clear();
 }
 
 void VideoCaptureDeviceLinux::StopAndDeAllocate() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!v4l2_thread_.IsRunning())
+  if (!capture_impl_)
     return;  // Wrong state.
-  v4l2_thread_.task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&V4L2CaptureDelegate::StopAndDeAllocate,
-                                capture_impl_->GetWeakPtr()));
-  v4l2_thread_.task_runner()->DeleteSoon(FROM_HERE, capture_impl_.release());
-  v4l2_thread_.Stop();
 
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&V4L2CaptureDelegate::StopAndDeAllocate,
+                                        capture_impl_->GetWeakPtr()));
+  task_runner_->DeleteSoon(FROM_HERE, std::move(capture_impl_));
   capture_impl_ = nullptr;
 }
 
 void VideoCaptureDeviceLinux::TakePhoto(TakePhotoCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(capture_impl_);
-  auto functor =
+  task_runner_->PostTask(
+      FROM_HERE,
       base::BindOnce(&V4L2CaptureDelegate::TakePhoto,
-                     capture_impl_->GetWeakPtr(), std::move(callback));
-  if (!v4l2_thread_.IsRunning()) {
-    // We have to wait until we get the device AllocateAndStart()ed.
-    photo_requests_queue_.push_back(std::move(functor));
-    return;
-  }
-  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
+                     capture_impl_->GetWeakPtr(), std::move(callback)));
 }
 
 void VideoCaptureDeviceLinux::GetPhotoState(GetPhotoStateCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto functor =
+  DCHECK(capture_impl_);
+  task_runner_->PostTask(
+      FROM_HERE,
       base::BindOnce(&V4L2CaptureDelegate::GetPhotoState,
-                     capture_impl_->GetWeakPtr(), std::move(callback));
-  if (!v4l2_thread_.IsRunning()) {
-    // We have to wait until we get the device AllocateAndStart()ed.
-    photo_requests_queue_.push_back(std::move(functor));
-    return;
-  }
-  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
+                     capture_impl_->GetWeakPtr(), std::move(callback)));
 }
 
 void VideoCaptureDeviceLinux::SetPhotoOptions(
     mojom::PhotoSettingsPtr settings,
     SetPhotoOptionsCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto functor = base::BindOnce(&V4L2CaptureDelegate::SetPhotoOptions,
+  DCHECK(capture_impl_);
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&V4L2CaptureDelegate::SetPhotoOptions,
                                 capture_impl_->GetWeakPtr(),
-                                std::move(settings), std::move(callback));
-  if (!v4l2_thread_.IsRunning()) {
-    // We have to wait until we get the device AllocateAndStart()ed.
-    photo_requests_queue_.push_back(std::move(functor));
-    return;
-  }
-  v4l2_thread_.task_runner()->PostTask(FROM_HERE, std::move(functor));
+                                std::move(settings), std::move(callback)));
 }
 
 void VideoCaptureDeviceLinux::SetRotation(int rotation) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(capture_impl_);
   rotation_ = rotation;
-  if (v4l2_thread_.IsRunning()) {
-    v4l2_thread_.task_runner()->PostTask(
-        FROM_HERE, base::BindOnce(&V4L2CaptureDelegate::SetRotation,
-                                  capture_impl_->GetWeakPtr(), rotation));
-  }
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&V4L2CaptureDelegate::SetRotation,
+                                        capture_impl_->GetWeakPtr(), rotation));
 }
 
 }  // namespace media
diff --git a/media/capture/video/linux/video_capture_device_linux.h b/media/capture/video/linux/video_capture_device_linux.h
index 08de3f0..0d2d413b 100644
--- a/media/capture/video/linux/video_capture_device_linux.h
+++ b/media/capture/video/linux/video_capture_device_linux.h
@@ -13,11 +13,7 @@
 #include <stdint.h>
 
 #include <memory>
-#include <vector>
 
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/threading/thread.h"
 #include "media/capture/video/linux/v4l2_capture_device_impl.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video_capture_types.h"
@@ -66,10 +62,7 @@
   // |v4l2_thread_|.
   std::unique_ptr<V4L2CaptureDelegate> capture_impl_;
 
-  // Photo-related requests waiting for |v4l2_thread_| to be active.
-  std::vector<base::OnceClosure> photo_requests_queue_;
-
-  base::Thread v4l2_thread_;  // Thread used for reading data from the device.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   // SetRotation() may get called even when the device is not started. When that
   // is the case we remember the value here and use it as soon as the device
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index 674e15c..fb75b04 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -236,16 +236,16 @@
   mojom::PhotoStatePtr state_;
 };
 
-base::test::SingleThreadTaskEnvironment::MainThreadType kMainThreadType =
+constexpr auto kMainThreadType =
 #if BUILDFLAG(IS_MAC)
     // Video capture code on MacOSX must run on a CFRunLoop enabled thread
     // for interaction with AVFoundation.
-    base::test::SingleThreadTaskEnvironment::MainThreadType::UI;
+    base::test::TaskEnvironment::MainThreadType::UI;
 #elif BUILDFLAG(IS_FUCHSIA)
     // FIDL APIs on Fuchsia requires IO thread.
-    base::test::SingleThreadTaskEnvironment::MainThreadType::IO;
+    base::test::TaskEnvironment::MainThreadType::IO;
 #else
-    base::test::SingleThreadTaskEnvironment::MainThreadType::DEFAULT;
+    base::test::TaskEnvironment::MainThreadType::DEFAULT;
 #endif
 
 }  // namespace
@@ -320,6 +320,7 @@
   }
 
   void TearDown() override {
+    task_environment_.RunUntilIdle();
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     chromeos::PowerManagerClient::Shutdown();
 #endif
@@ -469,7 +470,7 @@
 #if BUILDFLAG(IS_WIN)
   base::win::ScopedCOMInitializer initialize_com_;
 #endif
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::TaskEnvironment task_environment_;
   std::vector<VideoCaptureDeviceInfo> devices_info_;
   std::unique_ptr<base::RunLoop> run_loop_;
   scoped_refptr<base::TaskRunner> main_thread_task_runner_;
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index b67fb512..92bf8ff 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -453,22 +453,35 @@
 
   // We don't know if the decoder will error out on first decode yet. Save the
   // buffer to feed it to the fallback decoder later if needed.
-  if (!decoder_produced_a_frame_)
+  if (!decoder_produced_a_frame_) {
     pending_buffers_.push_back(buffer);
+  }
 
   // It's possible for a buffer to arrive from the demuxer right after the
   // fallback decoder successfully completed its initialization. At this point
   // |pending_buffers_| has already been copied to |fallback_buffers_| and we
   // need to append it ourselves.
-  if (!fallback_buffers_.empty()) {
-    fallback_buffers_.push_back(buffer);
+  if (!fallback_buffers_.empty() || fallback_buffers_being_decoded_ > 0) {
+    fallback_buffers_.push_back(std::exchange(buffer, nullptr));
 
-    scoped_refptr<DecoderBuffer> temp = std::move(fallback_buffers_.front());
-    fallback_buffers_.pop_front();
-    DecodeInternal(std::move(temp));
-  } else {
-    DecodeInternal(std::move(buffer));
+    // There may already be a pending buffer being decoded after decoder
+    // change. Since decoders can have different max decode requests, we need to
+    // make sure we can actually decode more buffers here.
+    if (!CanDecodeMore()) {
+      return;
+    }
   }
+
+  // TODO(https://crbug.com/1324732): We should DCHECK(CanDecodeMore()) here,
+  // but this breaks a number of tests.
+
+  if (!fallback_buffers_.empty()) {
+    buffer = std::move(fallback_buffers_.front());
+    fallback_buffers_.pop_front();
+    ++fallback_buffers_being_decoded_;
+  }
+
+  DecodeInternal(std::move(buffer));
 }
 
 template <DemuxerStream::Type StreamType>
@@ -562,6 +575,10 @@
       if (buffer_size > 0)
         traits_->ReportStatistics(statistics_cb_, buffer_size);
 
+      if (fallback_buffers_being_decoded_ > 0) {
+        --fallback_buffers_being_decoded_;
+      }
+
       if (state_ == STATE_NORMAL) {
         if (end_of_stream) {
           state_ = STATE_END_OF_STREAM;
@@ -671,6 +688,7 @@
   if (!fallback_buffers_.empty()) {
     scoped_refptr<DecoderBuffer> buffer = std::move(fallback_buffers_.front());
     fallback_buffers_.pop_front();
+    ++fallback_buffers_being_decoded_;
 
     // Decode the buffer without re-appending it to |pending_buffers_|.
     DecodeInternal(std::move(buffer));
@@ -714,7 +732,7 @@
     switch (status) {
       case DemuxerStream::kOk:
         // Save valid buffers to be consumed by the new decoder.
-        // |pending_buffers_| is copied to |fallback_buffers| in
+        // |pending_buffers_| is copied to |fallback_buffers_| in
         // OnDecoderSelected().
         pending_buffers_.push_back(std::move(buffer));
         break;
@@ -918,6 +936,7 @@
   // Make sure we read directly from the demuxer after a reset.
   fallback_buffers_.clear();
   pending_buffers_.clear();
+  fallback_buffers_being_decoded_ = 0;
 
   if (state_ != STATE_FLUSHING_DECODER) {
     state_ = STATE_NORMAL;
diff --git a/media/filters/decoder_stream.h b/media/filters/decoder_stream.h
index 8a39210..894949c 100644
--- a/media/filters/decoder_stream.h
+++ b/media/filters/decoder_stream.h
@@ -301,6 +301,8 @@
 
   bool encryption_type_reported_ = false;
 
+  int fallback_buffers_being_decoded_ = 0;
+
   // NOTE: Weak pointers must be invalidated before all other member variables.
   base::WeakPtrFactory<DecoderStream<StreamType>> weak_factory_{this};
 
diff --git a/media/filters/pipeline_controller.cc b/media/filters/pipeline_controller.cc
index 7ea76b5..26074c0 100644
--- a/media/filters/pipeline_controller.cc
+++ b/media/filters/pipeline_controller.cc
@@ -443,6 +443,11 @@
   Dispatch();
 }
 
+void PipelineController::OnExternalVideoFrameRequest() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  pipeline_->OnExternalVideoFrameRequest();
+}
+
 void PipelineController::FireOnTrackChangeCompleteForTesting(State set_to) {
   previous_track_change_state_ = set_to;
   OnTrackChangeComplete();
diff --git a/media/filters/pipeline_controller.h b/media/filters/pipeline_controller.h
index 425b1e1..116cae1 100644
--- a/media/filters/pipeline_controller.h
+++ b/media/filters/pipeline_controller.h
@@ -148,6 +148,7 @@
       const std::vector<MediaTrack::Id>& enabled_track_ids);
   void OnSelectedVideoTrackChanged(
       absl::optional<MediaTrack::Id> selected_track_id);
+  void OnExternalVideoFrameRequest();
 
   // Used to fire the OnTrackChangeComplete function which is captured in a
   // OnceCallback, and doesn't play nicely with gmock.
diff --git a/media/formats/hls/media_playlist.cc b/media/formats/hls/media_playlist.cc
index 08771b8..442df42 100644
--- a/media/formats/hls/media_playlist.cc
+++ b/media/formats/hls/media_playlist.cc
@@ -60,8 +60,11 @@
   absl::optional<XEndListTag> end_list_tag;
   absl::optional<XIFramesOnlyTag> i_frames_only_tag;
   absl::optional<XMediaSequenceTag> media_sequence_tag;
+  absl::optional<XDiscontinuitySequenceTag> discontinuity_sequence_tag;
   std::vector<MediaSegment> segments;
 
+  types::DecimalInteger discontinuity_sequence_number = 0;
+
   // If this media playlist was found through a multivariant playlist, it may
   // import variables from that playlist.
   if (parent_playlist) {
@@ -122,10 +125,19 @@
           break;
         }
         case MediaPlaylistTagName::kXDiscontinuity: {
-          auto error = ParseUniqueTag(*tag, discontinuity_tag);
-          if (error.has_value()) {
-            return std::move(error).value();
+          // Multiple occurrences of `EXT-X-DISCONTINUITY` per media segment are
+          // allowed, and each increments the segment's discontinuity sequence
+          // number by 1. The spec doesn't explicitly forbid this, and this
+          // seems to be how other HLS clients handle this scenario.
+          auto result = XDiscontinuityTag::Parse(*tag);
+          if (result.has_error()) {
+            return std::move(result).error();
           }
+
+          // Even if there was a previous discontinuity tag, overwrite the value
+          // and increment the discontinuity sequence number by 1.
+          discontinuity_tag = std::move(result).value();
+          discontinuity_sequence_number += 1;
           break;
         }
         case MediaPlaylistTagName::kXGap: {
@@ -168,6 +180,25 @@
           }
           break;
         }
+        case MediaPlaylistTagName::kXDiscontinuitySequence: {
+          auto error = ParseUniqueTag(*tag, discontinuity_sequence_tag);
+          if (error.has_value()) {
+            return std::move(error).value();
+          }
+
+          // This tag must appear before any media segment or
+          // EXT-X-DISCONTINUITY tag.
+          if (!segments.empty()) {
+            return ParseStatusCode::kMediaSegmentBeforeDiscontinuitySequenceTag;
+          }
+          if (discontinuity_sequence_number != 0) {
+            return ParseStatusCode::
+                kDiscontinuityTagBeforeDiscontinuitySequenceTag;
+          }
+
+          discontinuity_sequence_number = discontinuity_sequence_tag->number;
+          break;
+        }
       }
 
       continue;
@@ -198,8 +229,8 @@
         (media_sequence_tag ? media_sequence_tag->number : 0) + segments.size();
 
     segments.emplace_back(inf_tag->duration, media_sequence_number,
-                          std::move(segment_uri), discontinuity_tag.has_value(),
-                          gap_tag.has_value());
+                          discontinuity_sequence_number, std::move(segment_uri),
+                          discontinuity_tag.has_value(), gap_tag.has_value());
 
     // Reset per-segment tags
     inf_tag.reset();
diff --git a/media/formats/hls/media_playlist_test_builder.h b/media/formats/hls/media_playlist_test_builder.h
index cc8bae56..245d228 100644
--- a/media/formats/hls/media_playlist_test_builder.h
+++ b/media/formats/hls/media_playlist_test_builder.h
@@ -119,6 +119,15 @@
   EXPECT_EQ(segment.GetMediaSequenceNumber(), number) << from.ToString();
 }
 
+// Checks that the latest media segment has the given discontinuity sequence
+// number.
+inline void HasDiscontinuitySequenceNumber(types::DecimalInteger number,
+                                           const base::Location& from,
+                                           const MediaSegment& segment) {
+  EXPECT_EQ(segment.GetDiscontinuitySequenceNumber(), number)
+      << from.ToString();
+}
+
 // Checks that the latest media segment has the given URI.
 inline void HasUri(GURL uri,
                    const base::Location& from,
diff --git a/media/formats/hls/media_playlist_unittest.cc b/media/formats/hls/media_playlist_unittest.cc
index be1a525..774427b 100644
--- a/media/formats/hls/media_playlist_unittest.cc
+++ b/media/formats/hls/media_playlist_unittest.cc
@@ -50,30 +50,30 @@
   builder.AppendLine("video.ts");
   builder.ExpectAdditionalSegment();
   builder.ExpectSegment(HasDiscontinuity, false);
+  builder.ExpectSegment(HasDiscontinuitySequenceNumber, 0);
 
   builder.AppendLine("#EXT-X-DISCONTINUITY");
   builder.AppendLine("#EXTINF:9.9,\t");
   builder.AppendLine("video.ts");
   builder.ExpectAdditionalSegment();
   builder.ExpectSegment(HasDiscontinuity, true);
+  builder.ExpectSegment(HasDiscontinuitySequenceNumber, 1);
 
   // The discontinuity tag does not apply to subsequent segments
   builder.AppendLine("#EXTINF:9.9,\t");
   builder.AppendLine("video.ts");
   builder.ExpectAdditionalSegment();
   builder.ExpectSegment(HasDiscontinuity, false);
+  builder.ExpectSegment(HasDiscontinuitySequenceNumber, 1);
 
-  // The discontinuity tag may only appear once per segment
-  {
-    auto fork = builder;
-    fork.AppendLine("#EXT-X-DISCONTINUITY");
-    fork.AppendLine("#EXT-X-DISCONTINUITY");
-    fork.AppendLine("#EXTINF:9.9,\t");
-    fork.AppendLine("video.ts");
-    fork.ExpectAdditionalSegment();
-    fork.ExpectSegment(HasDiscontinuity, true);
-    fork.ExpectError(ParseStatusCode::kPlaylistHasDuplicateTags);
-  }
+  // The discontinuity tag may appear multiple times per segment
+  builder.AppendLine("#EXT-X-DISCONTINUITY");
+  builder.AppendLine("#EXT-X-DISCONTINUITY");
+  builder.AppendLine("#EXTINF:9.9,\t");
+  builder.AppendLine("video.ts");
+  builder.ExpectAdditionalSegment();
+  builder.ExpectSegment(HasDiscontinuity, true);
+  builder.ExpectSegment(HasDiscontinuitySequenceNumber, 3);
 
   builder.ExpectOk();
 }
@@ -573,16 +573,19 @@
     builder.ExpectAdditionalSegment();
     builder.ExpectSegment(HasUri, GURL("http://localhost/segment0.ts"));
     builder.ExpectSegment(HasMediaSequenceNumber, first_sequence_number);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber, 0);
 
     builder.AppendLine("#EXTINF:9.8,\t");
     builder.AppendLine("segment1.ts");
     builder.ExpectAdditionalSegment();
     builder.ExpectSegment(HasMediaSequenceNumber, first_sequence_number + 1);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber, 0);
 
     builder.AppendLine("#EXTINF:9.8,\t");
     builder.AppendLine("segment2.ts");
     builder.ExpectAdditionalSegment();
     builder.ExpectSegment(HasMediaSequenceNumber, first_sequence_number + 2);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber, 0);
   };
 
   // If the playlist does not contain the EXT-X-MEDIA-SEQUENCE tag, the default
@@ -613,4 +616,135 @@
   fork.ExpectOk();
 }
 
+TEST(HlsMediaPlaylistTest, XDiscontinuitySequenceTag) {
+  MediaPlaylistTestBuilder builder;
+  builder.AppendLine("#EXTM3U");
+  builder.AppendLine("#EXT-X-TARGETDURATION:10");
+
+  // The EXT-X-DISCONTINUITY-SEQUENCE tag must be a valid DecimalInteger
+  {
+    for (const base::StringPiece x : {"", ":-1", ":{$foo}", ":1.5", ":one"}) {
+      auto fork = builder;
+      fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE", x);
+      fork.ExpectError(ParseStatusCode::kMalformedTag);
+    }
+  }
+  // The EXT-X-DISCONTINUITY-SEQUENCE tag may not appear twice
+  {
+    auto fork = builder;
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:1");
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:1");
+    fork.ExpectError(ParseStatusCode::kPlaylistHasDuplicateTags);
+  }
+  {
+    auto fork = builder;
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:0");
+    fork.AppendLine("#EXT-X-DISCONTINUITY");
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:1");
+    fork.ExpectError(ParseStatusCode::kPlaylistHasDuplicateTags);
+  }
+  // The EXT-X-DISCONTINUITY-SEQUENCE tag must appear before any media segment
+  {
+    auto fork = builder;
+    fork.AppendLine("#EXTINF:9.8,\t");
+    fork.AppendLine("segment0.ts");
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:0");
+    fork.ExpectError(
+        ParseStatusCode::kMediaSegmentBeforeDiscontinuitySequenceTag);
+  }
+  // The EXT-X-DISCONTINUITY-SEQUENCE tag must appear before any
+  // EXT-X-DISCONTINUITY tag
+  {
+    auto fork = builder;
+    fork.AppendLine("#EXT-X-DISCONTINUITY");
+    fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:0");
+    fork.AppendLine("#EXTINF:9.8,\t");
+    fork.AppendLine("segment0.ts");
+    fork.ExpectError(
+        ParseStatusCode::kDiscontinuityTagBeforeDiscontinuitySequenceTag);
+  }
+
+  const auto fill_playlist = [](auto& builder, auto first_media_sequence_number,
+                                auto first_discontinuity_sequence_number) {
+    builder.AppendLine("#EXTINF:9.8,\t");
+    builder.AppendLine("segment0.ts");
+    builder.ExpectAdditionalSegment();
+    builder.ExpectSegment(HasUri, GURL("http://localhost/segment0.ts"));
+    builder.ExpectSegment(HasDiscontinuity, false);
+    builder.ExpectSegment(HasMediaSequenceNumber, first_media_sequence_number);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber,
+                          first_discontinuity_sequence_number);
+
+    builder.AppendLine("#EXT-X-DISCONTINUITY");
+    builder.AppendLine("#EXTINF:9.8,\t");
+    builder.AppendLine("segment1.ts");
+    builder.ExpectAdditionalSegment();
+    builder.ExpectSegment(HasDiscontinuity, true);
+    builder.ExpectSegment(HasMediaSequenceNumber,
+                          first_media_sequence_number + 1);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber,
+                          first_discontinuity_sequence_number + 1);
+
+    builder.AppendLine("#EXTINF:9.8,\t");
+    builder.AppendLine("segment2.ts");
+    builder.ExpectAdditionalSegment();
+    builder.ExpectSegment(HasDiscontinuity, false);
+    builder.ExpectSegment(HasMediaSequenceNumber,
+                          first_media_sequence_number + 2);
+    builder.ExpectSegment(HasDiscontinuitySequenceNumber,
+                          first_discontinuity_sequence_number + 1);
+  };
+
+  // If the playlist does not contain the EXT-X-DISCONTINUITY-SEQUENCE tag, the
+  // default starting value is 0.
+  auto fork = builder;
+  fill_playlist(fork, 0, 0);
+  fork.ExpectOk();
+
+  fork = builder;
+  fork.AppendLine("#EXT-X-MEDIA-SEQUENCE:10");
+  fill_playlist(fork, 10, 0);
+  fork.ExpectOk();
+
+  // If the playlist has the EXT-X-DISCONTINUITY-SEQUENCE tag, it specifies the
+  // starting value.
+  fork = builder;
+  fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:5");
+  fill_playlist(fork, 0, 5);
+  fork.ExpectOk();
+
+  fork = builder;
+  fork.AppendLine("#EXT-X-MEDIA-SEQUENCE:10");
+  fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:5");
+  fill_playlist(fork, 10, 5);
+  fork.ExpectOk();
+
+  // If the very first segment is a discontinuity, it should still have a
+  // subsequent discontinuity sequence number.
+  fork = builder;
+  fork.AppendLine("#EXT-X-MEDIA-SEQUENCE:10");
+  fork.AppendLine("#EXT-X-DISCONTINUITY");
+  fork.AppendLine("#EXTINF:9.2,\t");
+  fork.AppendLine("segment.ts");
+  fork.ExpectAdditionalSegment();
+  fork.ExpectSegment(HasDiscontinuity, true);
+  fork.ExpectSegment(HasMediaSequenceNumber, 10);
+  fork.ExpectSegment(HasDiscontinuitySequenceNumber, 1);
+  fill_playlist(fork, 11, 1);
+  fork.ExpectOk();
+
+  fork = builder;
+  fork.AppendLine("#EXT-X-MEDIA-SEQUENCE:10");
+  fork.AppendLine("#EXT-X-DISCONTINUITY-SEQUENCE:5");
+  fork.AppendLine("#EXT-X-DISCONTINUITY");
+  fork.AppendLine("#EXTINF:9.2,\t");
+  fork.AppendLine("segment.ts");
+  fork.ExpectAdditionalSegment();
+  fork.ExpectSegment(HasDiscontinuity, true);
+  fork.ExpectSegment(HasMediaSequenceNumber, 10);
+  fork.ExpectSegment(HasDiscontinuitySequenceNumber, 6);
+  fill_playlist(fork, 11, 6);
+  fork.ExpectOk();
+}
+
 }  // namespace media::hls
diff --git a/media/formats/hls/media_segment.cc b/media/formats/hls/media_segment.cc
index 50e0f04..9aa8416 100644
--- a/media/formats/hls/media_segment.cc
+++ b/media/formats/hls/media_segment.cc
@@ -11,11 +11,13 @@
 
 MediaSegment::MediaSegment(types::DecimalFloatingPoint duration,
                            types::DecimalInteger media_sequence_number,
+                           types::DecimalInteger discontinuity_sequence_number,
                            GURL uri,
                            bool has_discontinuity,
                            bool is_gap)
     : duration_(duration),
       media_sequence_number_(media_sequence_number),
+      discontinuity_sequence_number_(discontinuity_sequence_number),
       uri_(std::move(uri)),
       has_discontinuity_(has_discontinuity),
       is_gap_(is_gap) {}
diff --git a/media/formats/hls/media_segment.h b/media/formats/hls/media_segment.h
index d3031eb5..b97815fe 100644
--- a/media/formats/hls/media_segment.h
+++ b/media/formats/hls/media_segment.h
@@ -15,6 +15,7 @@
  public:
   MediaSegment(types::DecimalFloatingPoint duration,
                types::DecimalInteger media_sequence_number,
+               types::DecimalInteger discontinuity_sequence_number,
                GURL uri,
                bool has_discontinuity,
                bool is_gap);
@@ -32,6 +33,11 @@
     return media_sequence_number_;
   }
 
+  // Returns the discontinuity sequence number of this media segment.
+  types::DecimalInteger GetDiscontinuitySequenceNumber() const {
+    return discontinuity_sequence_number_;
+  }
+
   // The URI of the media resource. This will have already been resolved against
   // the playlist URI. This is guaranteed to be valid and non-empty, unless
   // `gap` is true, in which case this URI should not be used.
@@ -48,6 +54,7 @@
  private:
   types::DecimalFloatingPoint duration_;
   types::DecimalInteger media_sequence_number_;
+  types::DecimalInteger discontinuity_sequence_number_;
   GURL uri_;
   bool has_discontinuity_;
   bool is_gap_;
diff --git a/media/formats/hls/parse_status.cc b/media/formats/hls/parse_status.cc
index 0f10a2e..01f21b3 100644
--- a/media/formats/hls/parse_status.cc
+++ b/media/formats/hls/parse_status.cc
@@ -44,6 +44,8 @@
     PARSE_STATUS_CODE_CASE(kXStreamInfTagNotFollowedByUri);
     PARSE_STATUS_CODE_CASE(kVariantMissingStreamInfTag);
     PARSE_STATUS_CODE_CASE(kMediaSegmentBeforeMediaSequenceTag);
+    PARSE_STATUS_CODE_CASE(kMediaSegmentBeforeDiscontinuitySequenceTag);
+    PARSE_STATUS_CODE_CASE(kDiscontinuityTagBeforeDiscontinuitySequenceTag);
   }
 
   NOTREACHED();
diff --git a/media/formats/hls/parse_status.h b/media/formats/hls/parse_status.h
index ad974c93..77c8f8d 100644
--- a/media/formats/hls/parse_status.h
+++ b/media/formats/hls/parse_status.h
@@ -41,6 +41,8 @@
   kXStreamInfTagNotFollowedByUri,
   kVariantMissingStreamInfTag,
   kMediaSegmentBeforeMediaSequenceTag,
+  kMediaSegmentBeforeDiscontinuitySequenceTag,
+  kDiscontinuityTagBeforeDiscontinuitySequenceTag,
 };
 
 struct ParseStatusTraits {
diff --git a/media/formats/hls/tag_name.cc b/media/formats/hls/tag_name.cc
index 8c819f00..fb4283c 100644
--- a/media/formats/hls/tag_name.cc
+++ b/media/formats/hls/tag_name.cc
@@ -54,6 +54,8 @@
                  MultivariantPlaylistTagName::kXContentSteering),
     TagNameEntry("EXT-X-DEFINE", CommonTagName::kXDefine),
     TagNameEntry("EXT-X-DISCONTINUITY", MediaPlaylistTagName::kXDiscontinuity),
+    TagNameEntry("EXT-X-DISCONTINUITY-SEQUENCE",
+                 MediaPlaylistTagName::kXDiscontinuitySequence),
     TagNameEntry("EXT-X-ENDLIST", MediaPlaylistTagName::kXEndList),
     TagNameEntry("EXT-X-GAP", MediaPlaylistTagName::kXGap),
     TagNameEntry("EXT-X-I-FRAME-STREAM-INF",
diff --git a/media/formats/hls/tag_name.h b/media/formats/hls/tag_name.h
index 65db1d1..a89fee3d 100644
--- a/media/formats/hls/tag_name.h
+++ b/media/formats/hls/tag_name.h
@@ -58,7 +58,8 @@
   kXGap,
   kXPlaylistType,
   kXMediaSequence,
-  kMaxValue = kXMediaSequence,
+  kXDiscontinuitySequence,
+  kMaxValue = kXDiscontinuitySequence,
 };
 
 constexpr TagKind GetTagKind(CommonTagName) {
diff --git a/media/formats/hls/tags.cc b/media/formats/hls/tags.cc
index a040c1af..67b05c7a 100644
--- a/media/formats/hls/tags.cc
+++ b/media/formats/hls/tags.cc
@@ -449,4 +449,9 @@
   return ParseDecimalIntegerTag(tag, &XMediaSequenceTag::number);
 }
 
+ParseStatus::Or<XDiscontinuitySequenceTag> XDiscontinuitySequenceTag::Parse(
+    TagItem tag) {
+  return ParseDecimalIntegerTag(tag, &XDiscontinuitySequenceTag::number);
+}
+
 }  // namespace media::hls
diff --git a/media/formats/hls/tags.h b/media/formats/hls/tags.h
index ebed023..44ec810 100644
--- a/media/formats/hls/tags.h
+++ b/media/formats/hls/tags.h
@@ -178,6 +178,17 @@
   types::DecimalInteger number;
 };
 
+// Represents the contents of the #EXT-X-DISCONTINUITY-SEQUENCE tag.
+struct MEDIA_EXPORT XDiscontinuitySequenceTag {
+  static constexpr auto kName = MediaPlaylistTagName::kXDiscontinuitySequence;
+  static ParseStatus::Or<XDiscontinuitySequenceTag> Parse(TagItem);
+
+  // Indicates the discontinuity sequence number to assign to the first media
+  // segment in this playlist. These numbers are useful for synchronizing
+  // between variant stream timelines.
+  types::DecimalInteger number;
+};
+
 }  // namespace media::hls
 
 #endif  // MEDIA_FORMATS_HLS_TAGS_H_
diff --git a/media/formats/hls/tags_unittest.cc b/media/formats/hls/tags_unittest.cc
index 28e030c..3eebae59 100644
--- a/media/formats/hls/tags_unittest.cc
+++ b/media/formats/hls/tags_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "media/formats/hls/tags.h"
 
-#include <functional>
 #include <utility>
 
 #include "base/location.h"
@@ -114,8 +113,8 @@
 // There are a couple of tags that are defined simply as `#EXT-X-TAG:n` where
 // `n` must be a valid DecimalInteger. This helper provides coverage for those
 // tags.
-template <typename T, typename Fn>
-void RunDecimalIntegerTagTest(Fn getter_fn) {
+template <typename T>
+void RunDecimalIntegerTagTest(types::DecimalInteger T::*field) {
   // Content is required
   ErrorTest<T>(absl::nullopt, ParseStatusCode::kMalformedTag);
   ErrorTest<T>("", ParseStatusCode::kMalformedTag);
@@ -133,15 +132,15 @@
   ErrorTest<T>("{$X}", ParseStatusCode::kMalformedTag);
 
   auto tag = OkTest<T>("0");
-  EXPECT_EQ(getter_fn(tag), 0u);
+  EXPECT_EQ(tag.*field, 0u);
   tag = OkTest<T>("1");
-  EXPECT_EQ(getter_fn(tag), 1u);
+  EXPECT_EQ(tag.*field, 1u);
   tag = OkTest<T>("10");
-  EXPECT_EQ(getter_fn(tag), 10u);
+  EXPECT_EQ(tag.*field, 10u);
   tag = OkTest<T>("14");
-  EXPECT_EQ(getter_fn(tag), 14u);
+  EXPECT_EQ(tag.*field, 14u);
   tag = OkTest<T>("999999999999999999");
-  EXPECT_EQ(getter_fn(tag), 999999999999999999u);
+  EXPECT_EQ(tag.*field, 999999999999999999u);
 }
 
 VariableDictionary CreateBasicDictionary(
@@ -462,14 +461,18 @@
 TEST(HlsTagsTest, ParseXTargetDurationTag) {
   RunTagIdenficationTest<XTargetDurationTag>("#EXT-X-TARGETDURATION:10\n",
                                              "10");
-  RunDecimalIntegerTagTest<XTargetDurationTag>(
-      std::mem_fn(&XTargetDurationTag::duration));
+  RunDecimalIntegerTagTest(&XTargetDurationTag::duration);
 }
 
 TEST(HlsTagsTest, ParseXMediaSequenceTag) {
   RunTagIdenficationTest<XMediaSequenceTag>("#EXT-X-MEDIA-SEQUENCE:3\n", "3");
-  RunDecimalIntegerTagTest<XMediaSequenceTag>(
-      std::mem_fn(&XMediaSequenceTag::number));
+  RunDecimalIntegerTagTest(&XMediaSequenceTag::number);
+}
+
+TEST(HlsTagsTest, ParseXDiscontinuitySequenceTag) {
+  RunTagIdenficationTest<XDiscontinuitySequenceTag>(
+      "#EXT-X-DISCONTINUITY-SEQUENCE:3\n", "3");
+  RunDecimalIntegerTagTest(&XDiscontinuitySequenceTag::number);
 }
 
 }  // namespace media::hls
diff --git a/media/learning/common/target_histogram.h b/media/learning/common/target_histogram.h
index 6b99ad4..b173f44b 100644
--- a/media/learning/common/target_histogram.h
+++ b/media/learning/common/target_histogram.h
@@ -27,6 +27,11 @@
 struct COMPONENT_EXPORT(LEARNING_COMMON) TargetHistogramPair {
   TargetValue target_value;
   double count;
+
+  TargetHistogramPair() = default;
+
+  TargetHistogramPair(const TargetValue& value, double count)
+      : target_value(value), count(count) {}
 };
 
 // Histogram of target values that allows fractional counts.
diff --git a/media/learning/mojo/public/cpp/learning_mojom_traits.h b/media/learning/mojo/public/cpp/learning_mojom_traits.h
index 9203334..262a6f1 100644
--- a/media/learning/mojo/public/cpp/learning_mojom_traits.h
+++ b/media/learning/mojo/public/cpp/learning_mojom_traits.h
@@ -93,8 +93,8 @@
   static std::vector<media::learning::TargetHistogramPair> pairs(
       const media::learning::TargetHistogram& e) {
     std::vector<media::learning::TargetHistogramPair> pairs;
-    for (auto const& entry : e.counts_) {
-      pairs.push_back({entry.first, entry.second});
+    for (auto const& [target_val, count] : e.counts_) {
+      pairs.emplace_back(target_val, count);
     }
 
     return pairs;
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.cc b/media/mojo/clients/win/media_foundation_renderer_client.cc
index 22547623..74e33e7 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client.cc
+++ b/media/mojo/clients/win/media_foundation_renderer_client.cc
@@ -9,7 +9,6 @@
 #include "base/callback_helpers.h"
 #include "base/task/bind_post_task.h"
 #include "media/base/media_log.h"
-#include "media/base/media_switches.h"
 #include "media/base/win/mf_feature_checks.h"
 #include "media/base/win/mf_helpers.h"
 #include "media/mojo/mojom/speech_recognition_service.mojom.h"
@@ -87,9 +86,9 @@
   // protected content then start in Direct Composition mode, else start in
   // Frame Server mode. This behavior must match the logic in
   // MediaFoundationRenderer::Initialize.
-  auto rendering_strategy = kMediaFoundationClearRenderingStrategyParam.Get();
+  rendering_strategy_ = kMediaFoundationClearRenderingStrategyParam.Get();
   rendering_mode_ =
-      rendering_strategy ==
+      rendering_strategy_ ==
               MediaFoundationClearRenderingStrategy::kDirectComposition
           ? MediaFoundationRenderingMode::DirectComposition
           : MediaFoundationRenderingMode::FrameServer;
@@ -257,6 +256,16 @@
   std::move(change_completed_cb).Run();
 }
 
+void MediaFoundationRendererClient::OnExternalVideoFrameRequest() {
+  // A frame read back signal is currently treated as a permanent signal for
+  // the session so we only need to handle it the first time it is encountered.
+  if (!has_frame_read_back_signal_) {
+    has_frame_read_back_signal_ = true;
+    MEDIA_LOG(INFO, media_log_) << "Frame read back signal";
+    UpdateRenderMode();
+  }
+}
+
 // RendererClient implementation.
 
 void MediaFoundationRendererClient::OnError(PipelineStatus status) {
@@ -569,10 +578,28 @@
     const gpu::Mailbox& mailbox,
     bool promoted) {
   DCHECK(media_task_runner_->BelongsToCurrentThread());
-  MEDIA_LOG(INFO, media_log_) << "Overlay State promoted = " << promoted;
+  promoted_to_overlay_signal_ = promoted;
+  MEDIA_LOG(INFO, media_log_)
+      << "Overlay state signal, promoted = " << promoted;
+  UpdateRenderMode();
+}
 
-  if (promoted &&
-      rendering_mode_ != MediaFoundationRenderingMode::DirectComposition) {
+void MediaFoundationRendererClient::UpdateRenderMode() {
+  // We only change modes if we're using the dynamic rendering strategy and
+  // presenting clear content, so return early otherwise.
+  if (rendering_strategy_ != MediaFoundationClearRenderingStrategy::kDynamic ||
+      cdm_context_) {
+    return;
+  }
+
+  // Frame Server mode is required if we are not promoted to an overlay or if
+  // frame readback is required.
+  bool needs_frame_server =
+      has_frame_read_back_signal_ || !promoted_to_overlay_signal_;
+
+  if (!needs_frame_server && IsFrameServerMode()) {
+    MEDIA_LOG(INFO, media_log_) << "Switching to Direct Composition.";
+    // Switch to Frame Server Mode
     // Switch to Direct Composition mode.
     rendering_mode_ = MediaFoundationRenderingMode::DirectComposition;
     renderer_extension_->SetMediaFoundationRenderingMode(rendering_mode_);
@@ -586,17 +613,14 @@
     } else {
       sink_->PaintSingleFrame(dcomp_video_frame_, true);
     }
-  } else if (!promoted &&
-             rendering_mode_ != MediaFoundationRenderingMode::FrameServer) {
+  } else if (needs_frame_server && !IsFrameServerMode()) {
     // Switch to Frame Server mode.
+    MEDIA_LOG(INFO, media_log_) << "Switching to Frame Server.";
     rendering_mode_ = MediaFoundationRenderingMode::FrameServer;
     renderer_extension_->SetMediaFoundationRenderingMode(rendering_mode_);
     if (is_playing_) {
       sink_->Start(this);
     }
-  } else {
-    MEDIA_LOG(INFO, media_log_)
-        << "Overlay State Changed but there was no mode change.";
   }
 }
 
@@ -606,8 +630,7 @@
   // respond to promotion changes. If the rendering strategy is Direct
   // Composition or Frame Server then we do not need to listen & respond to
   // overlay state changes.
-  auto rendering_strategy = kMediaFoundationClearRenderingStrategyParam.Get();
-  if (rendering_strategy == MediaFoundationClearRenderingStrategy::kDynamic) {
+  if (rendering_strategy_ == MediaFoundationClearRenderingStrategy::kDynamic) {
     mailbox_ = mailbox;
     // 'observe_overlay_state_cb_' creates a content::OverlayStateObserver to
     // subscribe to overlay state information for the given 'mailbox' from the
diff --git a/media/mojo/clients/win/media_foundation_renderer_client.h b/media/mojo/clients/win/media_foundation_renderer_client.h
index bb68f92..8bbe617e 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client.h
+++ b/media/mojo/clients/win/media_foundation_renderer_client.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "media/base/media_resource.h"
+#include "media/base/media_switches.h"
 #include "media/base/renderer.h"
 #include "media/base/renderer_client.h"
 #include "media/base/video_renderer_sink.h"
@@ -88,6 +89,7 @@
   void OnSelectedVideoTracksChanged(
       const std::vector<DemuxerStream*>& enabled_tracks,
       base::OnceClosure change_completed_cb) override;
+  void OnExternalVideoFrameRequest() override;
 
   // RendererClient implementation.
   void OnError(PipelineStatus status) override;
@@ -135,6 +137,7 @@
   void SignalMediaPlayingStateChange(bool is_playing);
   void ObserveMailboxForOverlayState(const gpu::Mailbox& mailbox);
   void OnOverlayStateChanged(const gpu::Mailbox& mailbox, bool promoted);
+  void UpdateRenderMode();
 
   // This class is constructed on the main thread and used exclusively on the
   // media thread. Hence we store PendingRemotes so we can bind the Remotes
@@ -163,6 +166,8 @@
   bool output_size_updated_ = false;
   bool is_playing_ = false;
   bool has_video_ = false;
+  bool has_frame_read_back_signal_ = false;
+  bool promoted_to_overlay_signal_ = false;
   scoped_refptr<VideoFrame> dcomp_video_frame_;
   scoped_refptr<VideoFrame> next_video_frame_;
   gpu::Mailbox mailbox_;
@@ -171,6 +176,12 @@
   MediaFoundationRenderingMode rendering_mode_ =
       MediaFoundationRenderingMode::DirectComposition;
 
+  // Rendering strategy informs whether we enforce a rendering mode or allow
+  // dynamic transitions for Clear content. (Note: Protected content will always
+  // use Direct Composition mode).
+  MediaFoundationClearRenderingStrategy rendering_strategy_ =
+      MediaFoundationClearRenderingStrategy::kDirectComposition;
+
   PipelineStatusCallback init_cb_;
   raw_ptr<CdmContext> cdm_context_ = nullptr;
   CdmAttachedCB cdm_attached_cb_;
diff --git a/media/mojo/clients/win/media_foundation_renderer_client_factory.cc b/media/mojo/clients/win/media_foundation_renderer_client_factory.cc
index 5c15a834..0f3e560 100644
--- a/media/mojo/clients/win/media_foundation_renderer_client_factory.cc
+++ b/media/mojo/clients/win/media_foundation_renderer_client_factory.cc
@@ -100,7 +100,7 @@
       media_task_runner, media_log_->Clone(), std::move(mojo_renderer),
       std::move(renderer_extension_remote),
       std::move(client_extension_receiver), std::move(dcomp_texture_wrapper),
-      std::move(observe_overlay_state_cb_), video_renderer_sink,
+      observe_overlay_state_cb_, video_renderer_sink,
       std::move(media_foundation_renderer_observer_remote));
 }
 
diff --git a/media/mojo/services/webrtc_video_perf_history.cc b/media/mojo/services/webrtc_video_perf_history.cc
index f8c6cc1..ab5f84db2 100644
--- a/media/mojo/services/webrtc_video_perf_history.cc
+++ b/media/mojo/services/webrtc_video_perf_history.cc
@@ -313,16 +313,16 @@
     // inserted as a placeholder.
     std::vector<absl::optional<bool>> smooth_per_pixel;
     absl::optional<size_t> specific_key_index;
-    for (auto const& stats : *stats_collection) {
-      if (stats.first >= video_key.pixels && !specific_key_index) {
+    for (auto const& [key_index, video_stats_entry] : *stats_collection) {
+      if (key_index >= video_key.pixels && !specific_key_index) {
         specific_key_index = smooth_per_pixel.size();
-        if (stats.first > video_key.pixels) {
+        if (key_index > video_key.pixels) {
           // No exact match found, insert a nullopt.
           smooth_per_pixel.push_back(absl::nullopt);
         }
       }
       smooth_per_pixel.push_back(PredictSmooth(
-          video_key.is_decode_stats, stats.second, frames_per_second));
+          video_key.is_decode_stats, video_stats_entry, frames_per_second));
     }
     if (!specific_key_index) {
       // Pixels for the specific key is higher than any pixels number that
diff --git a/media/mojo/services/webrtc_video_perf_history_unittest.cc b/media/mojo/services/webrtc_video_perf_history_unittest.cc
index 89833cb..5fc59d6 100644
--- a/media/mojo/services/webrtc_video_perf_history_unittest.cc
+++ b/media/mojo/services/webrtc_video_perf_history_unittest.cc
@@ -118,12 +118,11 @@
 
     WebrtcVideoStatsDB::VideoStatsCollection collection;
     std::string key_filter = key.SerializeWithoutPixels();
-    for (auto const& entry : entries_) {
-      if (entry.first.rfind(key_filter, 0) == 0) {
-        absl::optional<int> pixels =
-            VideoDescKey::ParsePixelsFromKey(entry.first);
+    for (auto const& [str, video_stats_entry] : entries_) {
+      if (str.rfind(key_filter, 0) == 0) {
+        absl::optional<int> pixels = VideoDescKey::ParsePixelsFromKey(str);
         if (pixels) {
-          collection.insert({*pixels, std::move(entry.second)});
+          collection.insert({*pixels, std::move(video_stats_entry)});
         }
       }
     }
diff --git a/media/renderers/video_frame_rgba_to_yuva_converter.cc b/media/renderers/video_frame_rgba_to_yuva_converter.cc
index 9eecb98..80fa9431 100644
--- a/media/renderers/video_frame_rgba_to_yuva_converter.cc
+++ b/media/renderers/video_frame_rgba_to_yuva_converter.cc
@@ -118,8 +118,7 @@
                                  const gfx::ColorSpace& src_color_space,
                                  GrSurfaceOrigin src_surface_origin,
                                  const gpu::MailboxHolder& src_mailbox_holder,
-                                 VideoFrame* dst_video_frame,
-                                 gpu::SyncToken& completion_sync_token) {
+                                 VideoFrame* dst_video_frame) {
   DCHECK_EQ(dst_video_frame->format(), PIXEL_FORMAT_NV12);
 
   auto* ri = provider->RasterInterface();
@@ -218,12 +217,11 @@
   ri->WaitSyncTokenCHROMIUM(copy_to_gmb_done_sync_token.GetData());
 #endif  // BUILDFLAG(IS_WIN)
 
-  // Set `completion_sync_token` to mark the completion of the copy.
-  ri->GenSyncTokenCHROMIUM(completion_sync_token.GetData());
-
   // Make access to the `dst_video_frame` wait on copy completion. We also
   // update the ReleaseSyncToken here since it's used when the underlying
   // GpuMemoryBuffer and SharedImage resources are returned to the pool.
+  gpu::SyncToken completion_sync_token;
+  ri->GenSyncTokenCHROMIUM(completion_sync_token.GetData());
   SimpleSyncTokenClient simple_client(completion_sync_token);
   for (size_t plane = 0; plane < num_planes; ++plane)
     dst_video_frame->UpdateMailboxHolderSyncToken(plane, &simple_client);
diff --git a/media/renderers/video_frame_rgba_to_yuva_converter.h b/media/renderers/video_frame_rgba_to_yuva_converter.h
index 5ae5a30..3e04b99 100644
--- a/media/renderers/video_frame_rgba_to_yuva_converter.h
+++ b/media/renderers/video_frame_rgba_to_yuva_converter.h
@@ -16,7 +16,6 @@
 
 namespace gpu {
 struct MailboxHolder;
-struct SyncToken;
 }  // namespace gpu
 
 namespace viz {
@@ -39,8 +38,7 @@
     const gfx::ColorSpace& src_color_space,
     GrSurfaceOrigin src_surface_origin,
     const gpu::MailboxHolder& src_mailbox_holder,
-    media::VideoFrame* dst_video_frame,
-    gpu::SyncToken& completion_sync_token);
+    media::VideoFrame* dst_video_frame);
 
 }  // namespace media
 
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index e6e06a40..524eddd 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -690,7 +690,9 @@
 
     auto iter_and_result =
         policies_.insert(std::make_pair(policy.key, std::move(policy)));
-    DCHECK(iter_and_result.second);
+    // TODO(crbug.com/1326282): Change this to a DCHECK when we're sure the bug
+    // is fixed.
+    CHECK(iter_and_result.second);
 
     const NelPolicy& inserted_policy = iter_and_result.first->second;
     MaybeAddWildcardPolicy(inserted_policy.key, &inserted_policy);
@@ -873,7 +875,9 @@
 
     // TODO(chlily): Toss any expired policies we encounter.
     for (NelPolicy& policy : loaded_policies) {
-      AddPolicy(std::move(policy));
+      if (policies_.find(policy.key) == policies_.end()) {
+        AddPolicy(std::move(policy));
+      }
     }
     initialized_ = true;
     ExecuteBacklog();
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc
index cb634132..c9c0686d 100644
--- a/net/network_error_logging/network_error_logging_service_unittest.cc
+++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -1513,6 +1513,25 @@
   EXPECT_TRUE(store()->VerifyCommands(expected_commands));
 }
 
+TEST_P(NetworkErrorLoggingServiceTest, DuplicateEntriesInStore) {
+  if (!store())
+    return;
+
+  NetworkErrorLoggingService::NelPolicy policy1 = MakePolicy(kNik_, kOrigin_);
+  NetworkErrorLoggingService::NelPolicy policy2 = policy1;
+  std::vector<NetworkErrorLoggingService::NelPolicy> prestored_policies = {
+      policy1, policy2};
+  store()->SetPrestoredPolicies(std::move(prestored_policies));
+
+  // The first call to any of the public methods triggers a load.
+  service()->OnHeader(kNik_, kOrigin_, kServerIP_, kHeader_);
+  EXPECT_TRUE(store()->VerifyCommands(
+      {MockPersistentNelStore::Command::Type::LOAD_NEL_POLICIES}));
+  FinishLoading(/*load_success=*/true);
+
+  EXPECT_EQ(service()->GetPolicyKeysForTesting().size(), 1u);
+}
+
 // Same as the above test, except that all the tasks are queued until loading
 // is complete.
 TEST_P(NetworkErrorLoggingServiceTest, SendsCommandsToStoreDeferred) {
diff --git a/remoting/host/base/screen_controls.h b/remoting/host/base/screen_controls.h
index f6cd62a..5c67390f 100644
--- a/remoting/host/base/screen_controls.h
+++ b/remoting/host/base/screen_controls.h
@@ -5,6 +5,9 @@
 #ifndef REMOTING_HOST_BASE_SCREEN_CONTROLS_H_
 #define REMOTING_HOST_BASE_SCREEN_CONTROLS_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
+
 namespace remoting {
 
 class ScreenResolution;
@@ -14,10 +17,20 @@
  public:
   virtual ~ScreenControls() = default;
 
-  // If |resolution| is not empty, attempts to set new screen resolution in the
-  // session. If |resolution| is empty, attempts to restore the original screen
-  // resolution.
-  virtual void SetScreenResolution(const ScreenResolution& resolution) = 0;
+  // If |resolution| is not empty, attempts to set new screen resolution for
+  // the monitor |screen_id|. If |resolution| is empty, attempts to restore the
+  // original resolution of |screen_id|. Resizing a monitor may cause other
+  // monitors to be repositioned to accommodate the new size.
+  //
+  // If |screen_id| is not provided:
+  // If there is only 1 monitor, the monitor should be resized to |resolution|
+  // or restored if |resolution| is empty. If there is more than 1 monitor, the
+  // implementation should fall back to the behavior of older hosts (for
+  // example, the request may be ignored, or only the primary display may be
+  // resized).
+  virtual void SetScreenResolution(
+      const ScreenResolution& resolution,
+      absl::optional<webrtc::ScreenId> screen_id) = 0;
 };
 
 }  // namespace remoting
diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc
index 83554f4a..b1d9d8a 100644
--- a/remoting/host/client_session.cc
+++ b/remoting/host/client_session.cc
@@ -153,8 +153,12 @@
 
   // Try to match the client's resolution.
   // TODO(sergeyu): Pass clients DPI to the resizer.
-  screen_controls_->SetScreenResolution(ScreenResolution(
-      client_size, webrtc::DesktopVector(kDefaultDpi, kDefaultDpi)));
+  ScreenResolution screen_resolution(
+      client_size, webrtc::DesktopVector(kDefaultDpi, kDefaultDpi));
+  absl::optional<webrtc::ScreenId> screen_id;
+  if (resolution.has_screen_id())
+    screen_id = resolution.screen_id();
+  screen_controls_->SetScreenResolution(screen_resolution, screen_id);
 }
 
 void ClientSession::ControlVideo(const protocol::VideoControl& video_control) {
diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc
index 9c7a70fd..6a97d6ef 100644
--- a/remoting/host/desktop_session_agent.cc
+++ b/remoting/host/desktop_session_agent.cc
@@ -770,7 +770,7 @@
   CHECK(started_);
 
   if (screen_controls_)
-    screen_controls_->SetScreenResolution(resolution);
+    screen_controls_->SetScreenResolution(resolution, absl::nullopt);
 }
 
 void DesktopSessionAgent::SendToNetwork(std::unique_ptr<IPC::Message> message) {
diff --git a/remoting/host/fake_desktop_environment.cc b/remoting/host/fake_desktop_environment.cc
index 4c668553..d2c0a98 100644
--- a/remoting/host/fake_desktop_environment.cc
+++ b/remoting/host/fake_desktop_environment.cc
@@ -58,8 +58,8 @@
 FakeScreenControls::~FakeScreenControls() = default;
 
 void FakeScreenControls::SetScreenResolution(
-    const ScreenResolution& resolution) {
-}
+    const ScreenResolution& resolution,
+    absl::optional<webrtc::ScreenId> screen_id) {}
 
 FakeDesktopEnvironment::FakeDesktopEnvironment(
     scoped_refptr<base::SingleThreadTaskRunner> capture_thread,
diff --git a/remoting/host/fake_desktop_environment.h b/remoting/host/fake_desktop_environment.h
index 5f67fa7..0dda378 100644
--- a/remoting/host/fake_desktop_environment.h
+++ b/remoting/host/fake_desktop_environment.h
@@ -74,7 +74,8 @@
   ~FakeScreenControls() override;
 
   // ScreenControls implementation.
-  void SetScreenResolution(const ScreenResolution& resolution) override;
+  void SetScreenResolution(const ScreenResolution& resolution,
+                           absl::optional<webrtc::ScreenId> screen_id) override;
 };
 
 class FakeDesktopEnvironment : public DesktopEnvironment {
diff --git a/remoting/host/ipc_desktop_environment_unittest.cc b/remoting/host/ipc_desktop_environment_unittest.cc
index b4c4c52..511c28e 100644
--- a/remoting/host/ipc_desktop_environment_unittest.cc
+++ b/remoting/host/ipc_desktop_environment_unittest.cc
@@ -805,9 +805,10 @@
           this, &IpcDesktopEnvironmentTest::DeleteDesktopEnvironment));
 
   // Change the desktop resolution.
-  screen_controls_->SetScreenResolution(ScreenResolution(
-      webrtc::DesktopSize(100, 100),
-      webrtc::DesktopVector(96, 96)));
+  screen_controls_->SetScreenResolution(
+      ScreenResolution(webrtc::DesktopSize(100, 100),
+                       webrtc::DesktopVector(96, 96)),
+      absl::nullopt);
 }
 
 TEST_F(IpcDesktopEnvironmentTest, CheckUrlForwarderState) {
diff --git a/remoting/host/ipc_screen_controls.cc b/remoting/host/ipc_screen_controls.cc
index 0b27eec..93e6127 100644
--- a/remoting/host/ipc_screen_controls.cc
+++ b/remoting/host/ipc_screen_controls.cc
@@ -16,7 +16,9 @@
 IpcScreenControls::~IpcScreenControls() = default;
 
 void IpcScreenControls::SetScreenResolution(
-    const ScreenResolution& resolution) {
+    const ScreenResolution& resolution,
+    absl::optional<webrtc::ScreenId> screen_id) {
+  // TODO(crbug.com/1326339): Pass |screen_id| over IPC.
   desktop_session_proxy_->SetScreenResolution(resolution);
 }
 
diff --git a/remoting/host/ipc_screen_controls.h b/remoting/host/ipc_screen_controls.h
index 0e32807..473bf01 100644
--- a/remoting/host/ipc_screen_controls.h
+++ b/remoting/host/ipc_screen_controls.h
@@ -23,8 +23,9 @@
 
   ~IpcScreenControls() override;
 
-  // SessionController interface.
-  void SetScreenResolution(const ScreenResolution& resolution) override;
+  // ScreenControls interface.
+  void SetScreenResolution(const ScreenResolution& resolution,
+                           absl::optional<webrtc::ScreenId> screen_id) override;
 
  private:
   // Wraps the IPC channel to the desktop session agent.
diff --git a/remoting/host/resizing_host_observer.cc b/remoting/host/resizing_host_observer.cc
index 60d87df1..131db069 100644
--- a/remoting/host/resizing_host_observer.cc
+++ b/remoting/host/resizing_host_observer.cc
@@ -133,7 +133,8 @@
 }
 
 void ResizingHostObserver::SetScreenResolution(
-    const ScreenResolution& resolution) {
+    const ScreenResolution& resolution,
+    absl::optional<webrtc::ScreenId> screen_id) {
   // Get the current time. This function is called exactly once for each call
   // to SetScreenResolution to simplify the implementation of unit-tests.
   base::TimeTicks now = clock_->NowTicks();
@@ -152,7 +153,7 @@
     deferred_resize_timer_.Start(
         FROM_HERE, next_allowed_resize - now,
         base::BindOnce(&ResizingHostObserver::SetScreenResolution,
-                       weak_factory_.GetWeakPtr(), resolution));
+                       weak_factory_.GetWeakPtr(), resolution, screen_id));
     return;
   }
 
diff --git a/remoting/host/resizing_host_observer.h b/remoting/host/resizing_host_observer.h
index 4a1b8d1..80515e7a1 100644
--- a/remoting/host/resizing_host_observer.h
+++ b/remoting/host/resizing_host_observer.h
@@ -42,7 +42,8 @@
   ~ResizingHostObserver() override;
 
   // ScreenControls interface.
-  void SetScreenResolution(const ScreenResolution& resolution) override;
+  void SetScreenResolution(const ScreenResolution& resolution,
+                           absl::optional<webrtc::ScreenId> screen_id) override;
 
   // Provide a replacement for base::TimeTicks::Now so that this class can be
   // unit-tested in a timely manner. This function will be called exactly
diff --git a/remoting/host/resizing_host_observer_unittest.cc b/remoting/host/resizing_host_observer_unittest.cc
index 55a44c9..d5b49b25 100644
--- a/remoting/host/resizing_host_observer_unittest.cc
+++ b/remoting/host/resizing_host_observer_unittest.cc
@@ -114,7 +114,7 @@
   }
 
   void SetScreenResolution(const ScreenResolution& client_size) {
-    resizing_host_observer_->SetScreenResolution(client_size);
+    resizing_host_observer_->SetScreenResolution(client_size, absl::nullopt);
     if (auto_advance_clock_)
       clock_.Advance(base::Seconds(1));
   }
diff --git a/remoting/ios/DEPS b/remoting/ios/DEPS
index 3b6691ff..1ef6988 100644
--- a/remoting/ios/DEPS
+++ b/remoting/ios/DEPS
@@ -15,4 +15,5 @@
   "+third_party/ocmock",
   "+third_party/protobuf/src",
   "+services/network/public/cpp",
+  "+ui/display",
 ]
diff --git a/remoting/ios/facade/BUILD.gn b/remoting/ios/facade/BUILD.gn
index 9ae2b2b..1ed965e 100644
--- a/remoting/ios/facade/BUILD.gn
+++ b/remoting/ios/facade/BUILD.gn
@@ -56,6 +56,7 @@
     "//testing/gtest",
     "//third_party/ocmock",
     "//ui/base",
+    "//ui/display",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/remoting/ios/facade/host_list_service_unittest.mm b/remoting/ios/facade/host_list_service_unittest.mm
index d8304324..e0412c93 100644
--- a/remoting/ios/facade/host_list_service_unittest.mm
+++ b/remoting/ios/facade/host_list_service_unittest.mm
@@ -29,6 +29,7 @@
 #include "testing/platform_test.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/display/screen.h"
 
 #define EXPECT_HOST_LIST_STATE(expected) \
   EXPECT_EQ(expected, host_list_service_.state())
@@ -83,6 +84,7 @@
  private:
   base::CallbackListSubscription host_list_state_subscription_;
   base::CallbackListSubscription fetch_failure_subscription_;
+  display::ScopedNativeScreen screen_;
 };
 
 HostListServiceTest::HostListServiceTest()
diff --git a/services/service_manager/public/cpp/binder_registry.h b/services/service_manager/public/cpp/binder_registry.h
index 330eb42..16954a1c 100644
--- a/services/service_manager/public/cpp/binder_registry.h
+++ b/services/service_manager/public/cpp/binder_registry.h
@@ -73,6 +73,9 @@
       binders_.erase(it);
   }
 
+  // Removes all the binders from the registry.
+  void clear() { binders_.clear(); }
+
   // Returns true if an InterfaceBinder is registered for |interface_name|.
   bool CanBindInterface(const std::string& interface_name) const {
     auto it = binders_.find(interface_name);
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 46559937..c293a25 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8240,15 +8240,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8274,7 +8274,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8750,15 +8750,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -8784,7 +8784,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index c2c0f1e9..9af9dce 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46214,15 +46214,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46248,7 +46248,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46724,15 +46724,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -46758,7 +46758,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47238,15 +47238,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47272,7 +47272,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47748,15 +47748,15 @@
       {
         "args": [
           "--additional-apk=apks/ChromePublic.apk",
+          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/AOSP_SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -47782,7 +47782,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48330,15 +48330,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48364,7 +48364,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48840,15 +48840,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -48874,7 +48874,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49422,15 +49422,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
+          "--client-outdir",
+          "../../weblayer_instrumentation_test_M102/out/Release",
           "--implementation-outdir",
           ".",
           "--test-expectations",
           "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
-          "--client-outdir",
-          "../../weblayer_instrumentation_test_M102/out/Release",
           "--client-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49456,7 +49456,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49932,15 +49932,15 @@
       {
         "args": [
           "--additional-apk=apks/WebLayerShellSystemWebView.apk",
+          "--webview-apk-path=apks/SystemWebView.apk",
           "--test-runner-outdir",
           ".",
           "--client-outdir",
           ".",
-          "--test-expectations",
-          "../../weblayer/browser/android/javatests/skew/expectations.txt",
-          "--webview-apk-path=apks/SystemWebView.apk",
           "--implementation-outdir",
           "../../weblayer_instrumentation_test_M102/out/Release",
+          "--test-expectations",
+          "../../weblayer/browser/android/javatests/skew/expectations.txt",
           "--impl-version=102",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
@@ -49966,7 +49966,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.67"
+              "revision": "version:102.0.5005.68"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index c97ae0b..ac23e5e6 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -57268,4231 +57268,6 @@
       }
     ]
   },
-  "ios14-beta-simulator": {
-    "additional_compile_targets": [
-      "all"
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "absl_hardening_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "absl_hardening_tests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "base_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://base:base_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_crypto_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_crypto_tests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "boringssl_ssl_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "boringssl_ssl_tests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "components_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://components:components_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "crashpad_tests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crashpad_tests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "crypto_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "crypto_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://crypto:crypto_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "gfx_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gfx_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "google_apis_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "google_apis_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (3rd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPad Air (3rd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPad Air (3rd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_bookmarks_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Pro (12.9-inch) (2nd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_integration_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_integration_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (3rd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPad Air (3rd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPad Air (3rd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_settings_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_settings_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Pro (12.9-inch) (2nd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_signin_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_signin_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (3rd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPad Air (3rd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPad Air (3rd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_smoke_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_smoke_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Pro (12.9-inch) (2nd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_ui_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_ui_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_chrome_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (3rd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPad Air (3rd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPad Air (3rd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_chrome_web_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_chrome_web_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_components_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_components_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/components:ios_components_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_net_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test_id_prefix": "ninja://ios/net:ios_net_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_remoting_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_remoting_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air (3rd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPad Air (3rd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPad Air (3rd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_showcase_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_showcase_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_testing_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_testing_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/testing:ios_testing_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_inttests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad (6th generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPad (6th generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPad (6th generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Pro (12.9-inch) (2nd generation)",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 7",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone 7 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone 7 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest",
-          "--xcode-parallelization"
-        ],
-        "isolate_name": "ios_web_shell_eg2tests_module",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_shell_eg2tests_module iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_inttests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_inttests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ios_web_view_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ios_web_view_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "net_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "net_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://net:net_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "services_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "services_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://services:services_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "skia_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "skia_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://skia:skia_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "sql_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "sql_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://sql:sql_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPad Air 2",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPad Air 2 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPad Air 2 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone 6s Plus",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone 6s Plus 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone 6s Plus 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "ui_base_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "ui_base_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
-        "variant_id": "iPhone X 14.5"
-      },
-      {
-        "args": [
-          "--platform",
-          "iPhone X",
-          "--version",
-          "14.5",
-          "--out-dir",
-          "${ISOLATED_OUTDIR}",
-          "--xcode-build-version",
-          "13c100",
-          "--xctest"
-        ],
-        "isolate_name": "url_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "url_unittests iPhone X 14.5",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/mac_toolchain/${platform}",
-              "location": ".",
-              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Mac-11|Mac-10.16"
-            }
-          ],
-          "named_caches": [
-            {
-              "name": "xcode_ios_13c100",
-              "path": "Xcode.app"
-            },
-            {
-              "name": "runtime_ios_14_5",
-              "path": "Runtime-ios-14.5"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://url:url_unittests/",
-        "variant_id": "iPhone X 14.5"
-      }
-    ]
-  },
-  "ios14-sdk-simulator": {
-    "additional_compile_targets": [
-      "all"
-    ]
-  },
   "ios15-beta-simulator": {
     "additional_compile_targets": [
       "all"
@@ -78626,6 +74401,4231 @@
       }
     ]
   },
+  "ios16-beta-simulator": {
+    "additional_compile_targets": [
+      "all"
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "absl_hardening_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "absl_hardening_tests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "base_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "boringssl_crypto_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "boringssl_crypto_tests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "boringssl_ssl_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "boringssl_ssl_tests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "components_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://components:components_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "crashpad_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "crashpad_tests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "crypto_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "crypto_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://crypto:crypto_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "gfx_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gfx_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "google_apis_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "google_apis_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://google_apis:google_apis_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_bookmarks_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_bookmarks_eg2tests_module iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
+        "variant_id": "iPad Air (3rd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_bookmarks_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_bookmarks_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_bookmarks_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_bookmarks_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_integration_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_integration_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 8
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Pro (12.9-inch) (2nd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_integration_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_integration_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 8
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
+        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_integration_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_integration_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 8
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_integration_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_integration_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 8
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_integration_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_settings_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_settings_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_settings_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_settings_eg2tests_module iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
+        "variant_id": "iPad Air (3rd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_settings_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_settings_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_settings_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_settings_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_settings_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_signin_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_signin_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 5
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Pro (12.9-inch) (2nd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_signin_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_signin_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 5
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
+        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_signin_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_signin_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 5
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_signin_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_signin_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 5
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_signin_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_smoke_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_smoke_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_smoke_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_smoke_eg2tests_module iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
+        "variant_id": "iPad Air (3rd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_smoke_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_smoke_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_smoke_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_smoke_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_smoke_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_ui_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_ui_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Pro (12.9-inch) (2nd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_ui_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_ui_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
+        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_ui_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_ui_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_ui_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_ui_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_ui_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_chrome_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test:ios_chrome_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_web_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_web_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_web_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_web_eg2tests_module iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
+        "variant_id": "iPad Air (3rd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_web_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_web_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_chrome_web_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_chrome_web_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/chrome/test/earl_grey2:ios_chrome_web_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_components_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_components_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/components:ios_components_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_net_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_net_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
+        },
+        "test_id_prefix": "ninja://ios/net:ios_net_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_remoting_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_remoting_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_showcase_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_showcase_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_showcase_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_showcase_eg2tests_module iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
+        "variant_id": "iPad Air (3rd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_showcase_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_showcase_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_showcase_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_showcase_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/showcase:ios_showcase_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_testing_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_testing_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/testing:ios_testing_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_inttests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_inttests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad (6th generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_web_shell_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_shell_eg2tests_module iPad (6th generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
+        "variant_id": "iPad (6th generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Pro (12.9-inch) (2nd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_web_shell_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_shell_eg2tests_module iPad Pro (12.9-inch) (2nd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
+        "variant_id": "iPad Pro (12.9-inch) (2nd generation) 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 7",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_web_shell_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_shell_eg2tests_module iPhone 7 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
+        "variant_id": "iPhone 7 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest",
+          "--xcode-parallelization"
+        ],
+        "isolate_name": "ios_web_shell_eg2tests_module",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_shell_eg2tests_module iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web/shell/test:ios_web_shell_eg2tests_module/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web:ios_web_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_inttests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_inttests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_inttests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ios_web_view_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_web_view_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ios/web_view:ios_web_view_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "net_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "net_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://net:net_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "services_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "services_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://services:services_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "skia_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "skia_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://skia:skia_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "sql_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "sql_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://sql:sql_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPad Air 2",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPad Air 2 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPad Air 2 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone 6s Plus",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone 6s Plus 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone 6s Plus 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "ui_base_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ui_base_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/",
+        "variant_id": "iPhone X 14.5"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13c100",
+          "--xctest"
+        ],
+        "isolate_name": "url_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "url_unittests iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13c100",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://url:url_unittests/",
+        "variant_id": "iPhone X 14.5"
+      }
+    ]
+  },
+  "ios16-sdk-simulator": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  },
   "lacros-amd64-generic-rel (goma cache silo)": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2caedc82..0062ed1 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -6905,44 +6905,6 @@
       },
     },
 
-    'ios14_beta_simulator_tests': {
-      'ios_common_tests': {
-        'variants': [
-          'SIM_IPHONE_X_14_5',
-        ],
-      },
-      'ios_crash_unittests': {
-        'variants': [
-          'SIM_IPHONE_X_14_5',
-        ],
-      },
-      'ios_eg2_tests': {
-        'mixins': ['xcode_parallelization'],
-        'variants': [
-          'SIM_IPHONE_X_14_5',
-          'SIM_IPHONE_7_14_5',
-          'SIM_IPAD_AIR_3RD_GEN_14_5',
-          'SIM_IPAD_6_GEN_14_5',
-        ]
-      },
-      'ios_eg2_cq_tests': {
-        'mixins': ['xcode_parallelization'],
-        'variants': [
-          'SIM_IPHONE_7_14_5',
-          'SIM_IPHONE_X_14_5',
-          'SIM_IPAD_PRO_2ND_GEN_14_5',
-          'SIM_IPAD_6_GEN_14_5',
-        ]
-      },
-      'ios_screen_size_dependent_tests': {
-        'variants': [
-          'SIM_IPHONE_6S_PLUS_14_5',
-          'SIM_IPHONE_X_14_5',
-          'SIM_IPAD_AIR_2_14_5',
-        ],
-      },
-    },
-
     'ios15_beta_simulator_tests': {
       'ios_common_tests': {
         'variants': [
@@ -7048,6 +7010,44 @@
       },
     },
 
+    'ios16_beta_simulator_tests': {
+      'ios_common_tests': {
+        'variants': [
+          'SIM_IPHONE_X_14_5',
+        ],
+      },
+      'ios_crash_unittests': {
+        'variants': [
+          'SIM_IPHONE_X_14_5',
+        ],
+      },
+      'ios_eg2_tests': {
+        'mixins': ['xcode_parallelization'],
+        'variants': [
+          'SIM_IPHONE_X_14_5',
+          'SIM_IPHONE_7_14_5',
+          'SIM_IPAD_AIR_3RD_GEN_14_5',
+          'SIM_IPAD_6_GEN_14_5',
+        ]
+      },
+      'ios_eg2_cq_tests': {
+        'mixins': ['xcode_parallelization'],
+        'variants': [
+          'SIM_IPHONE_7_14_5',
+          'SIM_IPHONE_X_14_5',
+          'SIM_IPAD_PRO_2ND_GEN_14_5',
+          'SIM_IPAD_6_GEN_14_5',
+        ]
+      },
+      'ios_screen_size_dependent_tests': {
+        'variants': [
+          'SIM_IPHONE_6S_PLUS_14_5',
+          'SIM_IPHONE_X_14_5',
+          'SIM_IPAD_AIR_2_14_5',
+        ],
+      },
+    },
+
     'ios_asan_tests': {
       'ios_common_tests': {
         'variants': [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 00cc865..8218057 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -486,16 +486,16 @@
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/AOSP_SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -503,10 +503,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.67'
+          'revision': 'version:102.0.5005.68',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_10_AND_M_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -630,16 +630,16 @@
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
       '--client-outdir',
       '.',
-      '--test-expectations',
-      '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
       '--implementation-outdir',
       '../../weblayer_instrumentation_test_M102/out/Release',
-      '--impl-version=102'
+      '--test-expectations',
+      '../../weblayer/browser/android/javatests/skew/expectations.txt',
+      '--impl-version=102',
     ],
     'identifier': 'with_impl_from_102',
     'swarming': {
@@ -647,10 +647,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.67'
+          'revision': 'version:102.0.5005.68',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_IMPL_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
@@ -774,16 +774,16 @@
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_ONE_MILESTONE': {
     'args': [
+      '--webview-apk-path=apks/SystemWebView.apk',
       '--test-runner-outdir',
       '.',
+      '--client-outdir',
+      '../../weblayer_instrumentation_test_M102/out/Release',
       '--implementation-outdir',
       '.',
       '--test-expectations',
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
-      '--webview-apk-path=apks/SystemWebView.apk',
-      '--client-outdir',
-      '../../weblayer_instrumentation_test_M102/out/Release',
-      '--client-version=102'
+      '--client-version=102',
     ],
     'identifier': 'with_client_from_102',
     'swarming': {
@@ -791,10 +791,10 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.67'
+          'revision': 'version:102.0.5005.68',
         }
-      ]
-    }
+      ],
+    },
   },
   'WEBLAYER_CLIENT_SKEW_TESTS_NTH_MINUS_TWO_MILESTONE': {
     'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 6e44ffd3..c66301e 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3293,39 +3293,6 @@
           'isolated_scripts': 'ios_webkit_tot_tests',
         },
       },
-      # ios14-beta-sim compiles with xcode version n-1, but
-      # runs testers with xcode n during an xcode roll.
-      'ios14-beta-simulator': {
-        'additional_compile_targets': [
-          'all',
-        ],
-        'mixins': [
-          'has_native_resultdb_integration',
-          'mac_11_x64',
-          'mac_toolchain',
-          'out_dir_arg',
-          'xcode_13_main',
-          'xctest',
-        ],
-        'test_suites': {
-          'isolated_scripts': 'ios14_beta_simulator_tests'
-        },
-      },
-      # ios14-sdk-sim compiles with xcode version n, and runs
-      # testers with xcode n during an xcode roll.
-      'ios14-sdk-simulator': {
-        'additional_compile_targets': [
-          'all',
-        ],
-        'mixins': [
-          'has_native_resultdb_integration',
-          'mac_11_x64',
-          'mac_toolchain',
-          'out_dir_arg',
-          'xcode_13_main',
-          'xctest',
-        ],
-      },
       'ios15-beta-simulator': {
         'additional_compile_targets': [
           'all',
@@ -3363,6 +3330,39 @@
           'isolated_scripts': 'ios15_sdk_simulator_tests'
         },
       },
+      # ios16-beta-sim compiles with xcode version n-1, but
+      # runs testers with xcode n during an xcode roll.
+      'ios16-beta-simulator': {
+        'additional_compile_targets': [
+          'all',
+        ],
+        'mixins': [
+          'has_native_resultdb_integration',
+          'mac_11_x64',
+          'mac_toolchain',
+          'out_dir_arg',
+          'xcode_13_main',
+          'xctest',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'ios16_beta_simulator_tests'
+        },
+      },
+      # ios16-sdk-sim compiles with xcode version n, and runs
+      # testers with xcode n during an xcode roll.
+      'ios16-sdk-simulator': {
+        'additional_compile_targets': [
+          'all',
+        ],
+        'mixins': [
+          'has_native_resultdb_integration',
+          'mac_11_x64',
+          'mac_toolchain',
+          'out_dir_arg',
+          'xcode_13_main',
+          'xctest',
+        ],
+      },
       'lacros-amd64-generic-rel (goma cache silo)': {
         'additional_compile_targets': [
           'chrome',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 751c579..8d4a88f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -912,28 +912,6 @@
             ]
         }
     ],
-    "AutofillEnableSendingBcnInGetUploadDetails": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillEnableSendingBcnInGetUploadDetails"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillEnableSupportForMoreStructureInAddresses": [
         {
             "platforms": [
@@ -2898,6 +2876,51 @@
                     "disable_features": [
                         "NtpShoppingTasksModule"
                     ]
+                },
+                {
+                    "name": "Fre_20220509",
+                    "params": {
+                        "NtpChromeCartModuleAbandonedCartDiscountParam": "true",
+                        "NtpChromeCartModuleAbandonedCartDiscountUseUtmParam": "true",
+                        "NtpChromeCartModuleHeuristicsImprovementParam": "true",
+                        "NtpDriveModuleExperimentGroupParam": "foo:bar",
+                        "NtpDriveModuleManagedUsersOnlyParam": "true",
+                        "NtpModulesLoadTimeoutMillisecondsParam": "3000",
+                        "discount-fetch-delay": "1h",
+                        "use_sapi_v2": "true"
+                    },
+                    "enable_features": [
+                        "NtpChromeCartModule",
+                        "NtpDriveModule",
+                        "NtpModules",
+                        "NtpModulesFirstRunExperience",
+                        "NtpRecipeTasksModule"
+                    ],
+                    "disable_features": [
+                        "NtpShoppingTasksModule"
+                    ]
+                },
+                {
+                    "name": "Fre_Control_20220509",
+                    "params": {
+                        "NtpChromeCartModuleAbandonedCartDiscountParam": "true",
+                        "NtpChromeCartModuleAbandonedCartDiscountUseUtmParam": "true",
+                        "NtpChromeCartModuleHeuristicsImprovementParam": "true",
+                        "NtpDriveModuleExperimentGroupParam": "foo:bar",
+                        "NtpDriveModuleManagedUsersOnlyParam": "true",
+                        "NtpModulesLoadTimeoutMillisecondsParam": "3000",
+                        "discount-fetch-delay": "1h"
+                    },
+                    "enable_features": [
+                        "NtpChromeCartModule",
+                        "NtpDriveModule",
+                        "NtpModules",
+                        "NtpModulesFirstRunExperience",
+                        "NtpRecipeTasksModule"
+                    ],
+                    "disable_features": [
+                        "NtpShoppingTasksModule"
+                    ]
                 }
             ]
         }
@@ -3388,21 +3411,6 @@
             ]
         }
     ],
-    "EnhancedSafeBrowsing": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SafeBrowsingEnhancedProtection"
-                    ]
-                }
-            ]
-        }
-    ],
     "EstablishGpuChannelAsync": [
         {
             "platforms": [
@@ -3791,7 +3799,7 @@
                     "name": "Enabled",
                     "params": {
                         "enable_tab_group_auto_creation": "false",
-                        "show_open_in_tab_group_menu_item_first": "false"
+                        "show_open_in_tab_group_menu_item_first": "true"
                     },
                     "enable_features": [
                         "TabGridLayoutAndroid"
@@ -6670,6 +6678,21 @@
             ]
         }
     ],
+    "SafeBrowsingEnhancedProtection": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SafeBrowsingEnhancedProtection"
+                    ]
+                }
+            ]
+        }
+    ],
     "SafeBrowsingSendSampledPingsForProtegoAllowlistDomains": [
         {
             "platforms": [
@@ -6937,6 +6960,61 @@
             ]
         }
     ],
+    "SharedHighlightingRefinedMaxContextWords": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_MaxContextWords_15",
+                    "params": {
+                        "SharedHighlightingMaxContextWords": "15"
+                    },
+                    "enable_features": [
+                        "SharedHighlightingRefinedMaxContextWords"
+                    ]
+                },
+                {
+                    "name": "Enabled_MaxContextWords_5",
+                    "params": {
+                        "SharedHighlightingMaxContextWords": "5"
+                    },
+                    "enable_features": [
+                        "SharedHighlightingRefinedMaxContextWords"
+                    ]
+                },
+                {
+                    "name": "Enabled_MaxContextWords_10",
+                    "params": {
+                        "SharedHighlightingMaxContextWords": "10"
+                    },
+                    "enable_features": [
+                        "SharedHighlightingRefinedMaxContextWords"
+                    ]
+                },
+                {
+                    "name": "Enabled_MaxContextWords_20",
+                    "params": {
+                        "SharedHighlightingMaxContextWords": "20"
+                    },
+                    "enable_features": [
+                        "SharedHighlightingRefinedMaxContextWords"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "SharedHighlightingRefinedMaxContextWords"
+                    ]
+                }
+            ]
+        }
+    ],
     "SharedHighlightingTimeout": [
         {
             "platforms": [
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index b83361a..78ccefc8 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -751,7 +751,6 @@
       CrossOriginWindowAlert
       CrossOriginWindowConfirm
       CSSSelectorInternalMediaControlsOverlayCastButton
-      CustomCursorIntersectsViewport
       DeprecationExample
       DocumentDomainSettingWithoutOriginAgentClusterHeader
       EventPath
diff --git a/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom b/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
index 59b29d66..3f5d776 100644
--- a/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
+++ b/third_party/blink/public/mojom/chromeos/system_extensions/window_management/cros_window_management.mojom
@@ -94,4 +94,8 @@
 // Interface used by the browser to send the `start` event to Window Management
 // System Extensions service workers. This is only bound once when the system
 // extension is installed and the event is dispatched.
-interface CrosWindowManagementStartObserver {};
+interface CrosWindowManagementStartObserver {
+  // Notify the renderer that it should dispatch the `start` event. Currently,
+  // start is only dispatched when the System Extension is installed.
+  DispatchStartEvent();
+};
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 2597671..727e364 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -53,6 +53,7 @@
 import "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom";
 import "third_party/blink/public/mojom/input/focus_type.mojom";
 import "third_party/blink/public/mojom/input/scroll_direction.mojom";
+import "third_party/blink/public/mojom/messaging/delegated_capability.mojom";
 import "third_party/blink/public/mojom/navigation/navigation_api_history_entry_arrays.mojom";
 import "third_party/blink/public/mojom/navigation/navigation_policy.mojom";
 import "third_party/blink/public/mojom/loader/referrer.mojom";
@@ -142,6 +143,12 @@
   kRotate90Counterclockwise,
 };
 
+// The result of executing JavaScript within a frame.
+enum JavaScriptExecutionResultType {
+  kSuccess,
+  kException,
+};
+
 // The maximum number of characters of the document's title that we're willing
 // to accept in the browser process.
 const uint16 kMaxTitleChars = 4096; // 4 * 1024;
@@ -571,6 +578,12 @@
   // sends the new value to the browser. Also sent when a subframe's content
   // frame is changed.
   DidChangeSrcDoc(blink.mojom.FrameToken child_frame_token, string srcdoc_value);
+
+  // Notifies the browser that this frame received a message from a local frame
+  // with a delegated capability (https://wicg.github.io/capability-delegation).
+  // `delegated_capability` is the capability delegated via postMessage().
+  // RemoteFrameHost messages already signal the browser via RouteMessageEvent.
+  ReceivedDelegatedCapability(DelegatedCapability delegated_capability);
 };
 
 // Implemented in Blink, this interface defines frame-specific methods that will
@@ -772,13 +785,14 @@
 
   // ONLY FOR TESTS: Same as above but this can optionally trigger a fake user
   // activation notification to test functionalities that are gated by user
-  // activation.
+  // activation, and can block until a resulting promise is resolved.
   JavaScriptExecuteRequestForTests(
       mojo_base.mojom.BigString16 javascript,
-      bool wants_result,
       bool has_user_gesture,
+      bool resolve_promises,
       int32 world_id)
-      => (mojo_base.mojom.Value result);
+      => (JavaScriptExecutionResultType result_type,
+          mojo_base.mojom.Value result);
 
   // Same as JavaScriptExecuteRequest above except the script is run in the
   // isolated world specified by the fourth parameter.
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
index 1ef6199..821501f8 100644
--- a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -27,6 +27,7 @@
 import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
 import "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom";
 import "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom";
+import "services/service_manager/public/mojom/interface_provider.mojom";
 import "url/mojom/url.mojom";
 
 // Parameters to launch a service worker. This is passed from the browser to the
@@ -89,6 +90,9 @@
   // Used to talk to the service worker from the browser process.
   pending_receiver<ServiceWorker> service_worker_receiver;
 
+  // Used by the browser to request interfaces exposed by the renderer.
+  pending_receiver<service_manager.mojom.InterfaceProvider> interface_provider;
+
   // Cloned and passed to each controllee to directly dispatch events from the
   // controllees.
   pending_receiver<ControllerServiceWorker> controller_receiver;
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 5618778..aaa55b2 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -2120,7 +2120,7 @@
   kNoSysexWebMIDIWithoutPermission = 2770,
   kNoSysexWebMIDIOnInsecureOrigin = 2771,
   kOBSOLETE_ApplicationCacheInstalledButNoManifest = 2772,
-  kCustomCursorIntersectsViewport = 2776,
+  kOBSOLETE_CustomCursorIntersectsViewport = 2776,
   kOBSOLETE_ClientHintsLang = 2777,
   kLinkRelPreloadImageSrcset = 2778,
   kV8HTMLMediaElement_Remote_AttributeGetter = 2779,
diff --git a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
index a9b9d72..84133514b 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.cc
@@ -55,12 +55,20 @@
 }
 
 // static
-ScriptEvaluationResult ScriptEvaluationResult::FromClassicException() {
+ScriptEvaluationResult ScriptEvaluationResult::FromClassicExceptionRethrown() {
   return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
                                 ResultType::kException, {});
 }
 
 // static
+ScriptEvaluationResult ScriptEvaluationResult::FromClassicException(
+    v8::Local<v8::Value> exception) {
+  DCHECK(!exception.IsEmpty());
+  return ScriptEvaluationResult(mojom::blink::ScriptType::kClassic,
+                                ResultType::kException, exception);
+}
+
+// static
 ScriptEvaluationResult ScriptEvaluationResult::FromModuleException(
     v8::Local<v8::Value> exception) {
   DCHECK(!exception.IsEmpty());
@@ -102,6 +110,14 @@
   return value_;
 }
 
+v8::Local<v8::Value> ScriptEvaluationResult::GetExceptionForClassicForTesting()
+    const {
+  DCHECK_EQ(result_type_, ResultType::kException);
+  DCHECK(!value_.IsEmpty());
+
+  return value_;
+}
+
 ScriptPromise ScriptEvaluationResult::GetPromise(
     ScriptState* script_state) const {
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
index 18c77df..da3a586 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h
@@ -59,8 +59,11 @@
     // The script is evaluated and an exception is thrown.
     // Spec: #run-a-classic-script/#run-a-module-script return an abrupt
     //       completion.
-    // |value_| is the non-empty exception thrown for module scripts, or
-    // empty for classic scripts.
+    // |value_| is the non-empty exception thrown for
+    // - module scripts or
+    // - classic scripts with |RethrowErrorsOption::DoNotRethrow()|
+    // or empty for
+    // - classic scripts with |RethrowErrorsOption::Rethrow()|
     //
     // Note: The exception can be already caught and passed to
     // https://html.spec.whatwg.org/C/#report-the-error, instead of being
@@ -88,7 +91,9 @@
 
   static ScriptEvaluationResult FromClassicNotRun();
   static ScriptEvaluationResult FromClassicSuccess(v8::Local<v8::Value> value);
-  static ScriptEvaluationResult FromClassicException();
+  static ScriptEvaluationResult FromClassicExceptionRethrown();
+  static ScriptEvaluationResult FromClassicException(
+      v8::Local<v8::Value> exception);
   static ScriptEvaluationResult FromClassicAborted();
 
   static ScriptEvaluationResult FromModuleNotRun();
@@ -112,6 +117,10 @@
   // Can be called only when GetResultType() == kException.
   v8::Local<v8::Value> GetExceptionForModule() const;
 
+  // Returns the exception thrown for both module and classic scripts.
+  // Can be called only when GetResultType() == kException.
+  v8::Local<v8::Value> GetExceptionForClassicForTesting() const;
+
   // Returns the promise returned by #run-a-module-script.
   // Can be called only
   // - For module script with TLA is enabled, and
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index cd152a7..8762e2c 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -619,32 +619,35 @@
       // reported to WorkerGlobalScope.onerror via `TryCatch::SetVerbose(true)`
       // called at top-level worker script evaluation.
       try_catch.ReThrow();
-      return ScriptEvaluationResult::FromClassicException();
+      return ScriptEvaluationResult::FromClassicExceptionRethrown();
+    }
+
+    // Step 8.1.3. Otherwise, rethrow errors is false. Perform the following
+    // steps: [spec text]
+    if (!rethrow_errors.ShouldRethrow()) {
+      // #report-the-error for rethrow errors == true is already handled via
+      // `TryCatch::SetVerbose(true)` above.
+      return ScriptEvaluationResult::FromClassicException(
+          try_catch.Exception());
     }
   }
   // |v8::TryCatch| is (and should be) exited, before ThrowException() below.
 
-  if (rethrow_errors.ShouldRethrow()) {
-    // kDoNotSanitize case is processed and early-exited above.
-    DCHECK_EQ(sanitize_script_errors, SanitizeScriptErrors::kSanitize);
+  // kDoNotSanitize case is processed and early-exited above.
+  DCHECK(rethrow_errors.ShouldRethrow());
+  DCHECK_EQ(sanitize_script_errors, SanitizeScriptErrors::kSanitize);
 
-    // Step 8.2. If rethrow errors is true and script's muted errors is
-    // true, then: [spec text]
-    //
-    // Step 8.2.2. Throw a "NetworkError" DOMException. [spec text]
-    //
-    // We don't supply any message here to avoid leaking details of muted
-    // errors.
-    V8ThrowException::ThrowException(
-        isolate, V8ThrowDOMException::CreateOrEmpty(
-                     isolate, DOMExceptionCode::kNetworkError,
-                     rethrow_errors.Message()));
-    return ScriptEvaluationResult::FromClassicException();
-  }
-
-  // #report-the-error for rethrow errors == true is already handled via
-  // |TryCatch::SetVerbose(true)| above.
-  return ScriptEvaluationResult::FromClassicException();
+  // Step 8.2. If rethrow errors is true and script's muted errors is true,
+  // then: [spec text]
+  //
+  // Step 8.2.2. Throw a "NetworkError" DOMException. [spec text]
+  //
+  // We don't supply any message here to avoid leaking details of muted errors.
+  V8ThrowException::ThrowException(
+      isolate,
+      V8ThrowDOMException::CreateOrEmpty(
+          isolate, DOMExceptionCode::kNetworkError, rethrow_errors.Message()));
+  return ScriptEvaluationResult::FromClassicExceptionRethrown();
 }
 
 v8::MaybeLocal<v8::Value> V8ScriptRunner::CompileAndRunInternalScript(
diff --git a/third_party/blink/renderer/controller/blink_initializer.cc b/third_party/blink/renderer/controller/blink_initializer.cc
index 36cd94d..79e4ce5 100644
--- a/third_party/blink/renderer/controller/blink_initializer.cc
+++ b/third_party/blink/renderer/controller/blink_initializer.cc
@@ -131,6 +131,13 @@
 #endif  // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_ARM64) &&
         // BUILDFLAG(IS_WIN)
 
+#if defined(USE_BLINK_EXTENSIONS_CHROMEOS)
+  // ChromeOSExtensions::Initialize() initializes strings which must be done
+  // before calling CoreInitializer::Initialize() which is called by
+  // GetBlinkInitializer().Initialize() below.
+  ChromeOSExtensions::Initialize();
+#endif
+
   // BlinkInitializer::Initialize() must be called before InitializeMainThread
   GetBlinkInitializer().Initialize();
 
@@ -146,10 +153,6 @@
   g_end_of_task_runner = new EndOfTaskRunner;
   Thread::Current()->AddTaskObserver(g_end_of_task_runner);
 
-#if defined(USE_BLINK_EXTENSIONS_CHROMEOS)
-  ChromeOSExtensions::Initialize();
-#endif
-
 #if BUILDFLAG(IS_ANDROID)
   // Initialize CrashMemoryMetricsReporterImpl in order to assure that memory
   // allocation does not happen in OnOOMCallback.
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
index 1853b70..32752e70 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.cc
@@ -128,7 +128,15 @@
 
 TextFragmentSelectorGenerator::TextFragmentSelectorGenerator(
     LocalFrame* main_frame)
-    : frame_(main_frame) {}
+    : frame_(main_frame) {
+  if (base::FeatureList::IsEnabled(
+          shared_highlighting::kSharedHighlightingRefinedMaxContextWords)) {
+    max_context_words_ =
+        shared_highlighting::kSharedHighlightingMaxContextWords.Get();
+  } else {
+    max_context_words_ = kMaxContextWords;
+  }
+}
 
 void TextFragmentSelectorGenerator::Generate(const RangeInFlatTree& range,
                                              GenerateCallback callback) {
@@ -564,7 +572,7 @@
   DCHECK(selector_);
 
   // Give up if context is already too long.
-  if (num_context_words_ >= kMaxContextWords) {
+  if (num_context_words_ >= max_context_words_) {
     state_ = kFailure;
     error_ = LinkGenerationError::kContextLimitReached;
     return;
diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
index 0bbc96f..7ceae62f 100644
--- a/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h
@@ -196,6 +196,10 @@
 
   int num_range_words_ = 0;
 
+  // Indicates the Max Context Words allowed for the
+  // SharedHighlightsMaxContextWords experiment
+  int max_context_words_ = 10;
+
   int iteration_ = 0;
   base::TimeTicks generation_start_time_;
 
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
index 9ebf78a..42f7ad68 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.cc
@@ -92,9 +92,6 @@
       return DeprecationInfo::WithTranslation(
           feature, DeprecationIssueType::
                        kCSSSelectorInternalMediaControlsOverlayCastButton);
-    case WebFeature::kCustomCursorIntersectsViewport:
-      return DeprecationInfo::WithTranslation(
-          feature, DeprecationIssueType::kCustomCursorIntersectsViewport);
     case WebFeature::kDeprecationExample:
       return DeprecationInfo::WithTranslation(
           feature, DeprecationIssueType::kDeprecationExample);
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index a252031..28f9aae 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1064,6 +1064,15 @@
                                       source->GetStorageKey(), UkmSourceID(),
                                       GetStorageKey(), UkmRecorder());
 
+  // Notify the host if the message contained a delegated capability. That state
+  // should be tracked by the browser, and messages from remote hosts already
+  // signal the browser via RemoteFrameHost's RouteMessageEvent.
+  if (posted_message->delegated_capability !=
+      mojom::blink::DelegatedCapability::kNone) {
+    GetFrame()->GetLocalFrameHostRemote().ReceivedDelegatedCapability(
+        posted_message->delegated_capability);
+  }
+
   // Convert the posted message to a MessageEvent so it can be unpacked for
   // local dispatch.
   MessageEvent* event = MessageEvent::Create(
@@ -1191,7 +1200,6 @@
           mojom::blink::DelegatedCapability::kFullscreenRequest) {
     UseCounter::Count(this,
                       WebFeature::kCapabilityDelegationOfFullscreenRequest);
-    // TODO(crbug.com/1293083): Activate a corresponding token in the browser.
     fullscreen_request_token_.Activate();
   }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index e0795c17..e4a632d 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/ignore_opens_during_unload_count_incrementer.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
@@ -168,10 +169,9 @@
 }
 
 base::Value GetJavaScriptExecutionResult(v8::Local<v8::Value> result,
-                                         LocalFrame* local_frame,
+                                         v8::Local<v8::Context> context,
                                          WebV8ValueConverter* converter) {
   if (!result.IsEmpty()) {
-    v8::Local<v8::Context> context = MainWorldScriptContext(local_frame);
     v8::Context::Scope context_scope(context);
     std::unique_ptr<base::Value> new_value =
         converter->FromV8Value(result, context);
@@ -335,6 +335,115 @@
     metadata->image = document.CompleteURL(element.Content());
 }
 
+// Convert the error to a string so it can be sent back to the test.
+//
+// We try to use .stack property so that the error message contains a stack
+// trace, but otherwise fallback to .toString().
+v8::Local<v8::String> ErrorToString(ScriptState* script_state,
+                                    v8::Local<v8::Value> error) {
+  if (!error.IsEmpty()) {
+    v8::Local<v8::Context> context = script_state->GetContext();
+    v8::Local<v8::Value> value =
+        v8::TryCatch::StackTrace(context, error).FromMaybe(error);
+    v8::Local<v8::String> value_string;
+    if (value->ToString(context).ToLocal(&value_string))
+      return value_string;
+  }
+
+  v8::Isolate* isolate = script_state->GetIsolate();
+  return v8::String::NewFromUtf8Literal(isolate, "Unknown Failure");
+}
+
+class JavaScriptExecuteRequestForTestsHandler
+    : public GarbageCollected<JavaScriptExecuteRequestForTestsHandler> {
+ public:
+  class PromiseCallback : public ScriptFunction::Callable {
+   public:
+    PromiseCallback(JavaScriptExecuteRequestForTestsHandler& handler,
+                    mojom::blink::JavaScriptExecutionResultType type)
+        : handler_(handler), type_(type) {}
+
+    ScriptValue Call(ScriptState* script_state, ScriptValue value) override {
+      DCHECK(script_state);
+      if (type_ == mojom::blink::JavaScriptExecutionResultType::kSuccess)
+        handler_->SendSuccess(script_state, value.V8Value());
+      else
+        handler_->SendException(script_state, value.V8Value());
+      return {};
+    }
+
+    void Trace(Visitor* visitor) const override {
+      visitor->Trace(handler_);
+      ScriptFunction::Callable::Trace(visitor);
+    }
+
+   private:
+    Member<JavaScriptExecuteRequestForTestsHandler> handler_;
+    const mojom::blink::JavaScriptExecutionResultType type_;
+  };
+
+  explicit JavaScriptExecuteRequestForTestsHandler(
+      LocalFrameMojoHandler::JavaScriptExecuteRequestForTestsCallback callback)
+      : callback_(std::move(callback)) {}
+
+  ~JavaScriptExecuteRequestForTestsHandler() {
+    if (callback_) {
+      std::move(callback_).Run(
+          mojom::blink::JavaScriptExecutionResultType::kException,
+          base::Value(
+              "JavaScriptExecuteRequestForTestsHandler was destroyed without "
+              "running the callback. This is usually caused by Promise "
+              "resolution functions getting destroyed without being called."));
+    }
+  }
+
+  ScriptFunction* CreateResolveCallback(ScriptState* script_state,
+                                        LocalFrame* frame) {
+    return MakeGarbageCollected<ScriptFunction>(
+        script_state,
+        MakeGarbageCollected<PromiseCallback>(
+            *this, mojom::blink::JavaScriptExecutionResultType::kSuccess));
+  }
+
+  ScriptFunction* CreateRejectCallback(ScriptState* script_state,
+                                       LocalFrame* frame) {
+    return MakeGarbageCollected<ScriptFunction>(
+        script_state,
+        MakeGarbageCollected<PromiseCallback>(
+            *this, mojom::blink::JavaScriptExecutionResultType::kException));
+  }
+
+  void SendSuccess(ScriptState* script_state, v8::Local<v8::Value> value) {
+    SendResponse(script_state,
+                 mojom::blink::JavaScriptExecutionResultType::kSuccess, value);
+  }
+
+  void SendException(ScriptState* script_state, v8::Local<v8::Value> error) {
+    SendResponse(script_state,
+                 mojom::blink::JavaScriptExecutionResultType::kException,
+                 ErrorToString(script_state, error));
+  }
+
+  void Trace(Visitor* visitor) const {}
+
+ private:
+  void SendResponse(ScriptState* script_state,
+                    mojom::blink::JavaScriptExecutionResultType type,
+                    v8::Local<v8::Value> value) {
+    std::unique_ptr<WebV8ValueConverter> converter =
+        Platform::Current()->CreateWebV8ValueConverter();
+    converter->SetDateAllowed(true);
+    converter->SetRegExpAllowed(true);
+
+    CHECK(callback_) << "Promise resolved twice";
+    std::move(callback_).Run(
+        type, GetJavaScriptExecutionResult(value, script_state->GetContext(),
+                                           converter.get()));
+  }
+
+  LocalFrameMojoHandler::JavaScriptExecuteRequestForTestsCallback callback_;
+};
+
 }  // namespace
 
 ActiveURLMessageFilter::~ActiveURLMessageFilter() {
@@ -871,8 +980,9 @@
            .ToLocal(&result)) {
     std::move(callback).Run({});
   } else if (wants_result) {
+    v8::Local<v8::Context> context = MainWorldScriptContext(frame_);
     std::move(callback).Run(
-        GetJavaScriptExecutionResult(result, frame_, converter.get()));
+        GetJavaScriptExecutionResult(result, context, converter.get()));
   } else {
     std::move(callback).Run({});
   }
@@ -903,8 +1013,9 @@
     converter->SetDateAllowed(true);
     converter->SetRegExpAllowed(true);
 
+    v8::Local<v8::Context> context = MainWorldScriptContext(frame_);
     std::move(callback).Run(
-        GetJavaScriptExecutionResult(result, frame_, converter.get()));
+        GetJavaScriptExecutionResult(result, context, converter.get()));
   } else {
     std::move(callback).Run({});
   }
@@ -915,8 +1026,8 @@
 
 void LocalFrameMojoHandler::JavaScriptExecuteRequestForTests(
     const String& javascript,
-    bool wants_result,
     bool has_user_gesture,
+    bool resolve_promises,
     int32_t world_id,
     JavaScriptExecuteRequestForTestsCallback callback) {
   TRACE_EVENT_INSTANT0("test_tracing", "JavaScriptExecuteRequestForTests",
@@ -927,8 +1038,13 @@
   if (has_user_gesture)
     NotifyUserActivation(mojom::blink::UserActivationNotificationType::kTest);
 
-  v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate());
-  v8::Local<v8::Value> result;
+  v8::Isolate* isolate = ToIsolate(frame_);
+  ScriptState* script_state =
+      (world_id == DOMWrapperWorld::kMainWorldId)
+          ? ToScriptStateForMainWorld(frame_)
+          : ToScriptState(frame_, *DOMWrapperWorld::EnsureIsolatedWorld(
+                                      isolate, world_id));
+  ScriptState::Scope script_state_scope(script_state);
 
   // `kDoNotSanitize` is used because this is only for tests and some tests
   // need `kDoNotSanitize` for dynamic imports.
@@ -936,28 +1052,40 @@
       javascript, ScriptSourceLocationType::kUnknown,
       SanitizeScriptErrors::kDoNotSanitize);
 
-  if (world_id == DOMWrapperWorld::kMainWorldId) {
-    result =
-        script->RunScriptAndReturnValue(DomWindow()).GetSuccessValueOrEmpty();
-  } else {
-    CHECK_GT(world_id, DOMWrapperWorld::kMainWorldId);
-    CHECK_LT(world_id, DOMWrapperWorld::kDOMWrapperWorldEmbedderWorldIdLimit);
-    result =
-        script->RunScriptInIsolatedWorldAndReturnValue(DomWindow(), world_id)
-            .GetSuccessValueOrEmpty();
-  }
+  ScriptEvaluationResult result =
+      script->RunScriptOnScriptStateAndReturnValue(script_state);
 
-  if (wants_result) {
-    std::unique_ptr<WebV8ValueConverter> converter =
-        Platform::Current()->CreateWebV8ValueConverter();
-    converter->SetDateAllowed(true);
-    converter->SetRegExpAllowed(true);
+  auto* handler = MakeGarbageCollected<JavaScriptExecuteRequestForTestsHandler>(
+      std::move(callback));
+  v8::Local<v8::Value> error;
+  switch (result.GetResultType()) {
+    case ScriptEvaluationResult::ResultType::kSuccess: {
+      v8::Local<v8::Value> value = result.GetSuccessValue();
+      if (resolve_promises && !value.IsEmpty() && value->IsPromise()) {
+        ScriptPromise promise = ScriptPromise::Cast(script_state, value);
+        promise.Then(handler->CreateResolveCallback(script_state, frame_),
+                     handler->CreateRejectCallback(script_state, frame_));
+      } else {
+        handler->SendSuccess(script_state, value);
+      }
+      return;
+    }
 
-    std::move(callback).Run(
-        GetJavaScriptExecutionResult(result, frame_, converter.get()));
-  } else {
-    std::move(callback).Run({});
+    case ScriptEvaluationResult::ResultType::kException:
+      error = result.GetExceptionForClassicForTesting();
+      break;
+
+    case ScriptEvaluationResult::ResultType::kAborted:
+      error = v8::String::NewFromUtf8Literal(isolate, "Script aborted");
+      break;
+
+    case ScriptEvaluationResult::ResultType::kNotRun:
+      error = v8::String::NewFromUtf8Literal(isolate, "Script not run");
+      break;
   }
+  DCHECK_NE(result.GetResultType(),
+            ScriptEvaluationResult::ResultType::kSuccess);
+  handler->SendException(script_state, error);
 }
 
 void LocalFrameMojoHandler::JavaScriptExecuteRequestInIsolatedWorld(
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 224cfd8..cb186a7 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -205,11 +205,8 @@
   // The process where this frame actually lives won't have sufficient
   // information to upgrade the url, since it won't have access to the
   // origin context. Do it now.
-  const FetchClientSettingsObject* fetch_client_settings_object = nullptr;
-  if (window) {
-    fetch_client_settings_object =
-        &window->Fetcher()->GetProperties().GetFetchClientSettingsObject();
-  }
+  const FetchClientSettingsObject* fetch_client_settings_object =
+      &window->Fetcher()->GetProperties().GetFetchClientSettingsObject();
   MixedContentChecker::UpgradeInsecureRequest(
       frame_request.GetResourceRequest(), fetch_client_settings_object, window,
       frame_request.GetFrameType(),
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 5027aff..bc58535 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -709,9 +709,9 @@
     LocalFrame& frame = *window.GetFrame();
     frame.GetChromeClient().EnterFullscreen(frame, options, request_type);
 
-    // After the first fullscreen request, the user activation should be
-    // consumed, and the following fullscreen requests should receive an error.
     if (!for_cross_process_descendant) {
+      // Consume any transient user activation and delegated fullscreen token.
+      // AllowedToRequestFullscreen() enforces algorithm requirements earlier.
       LocalFrame::ConsumeTransientUserActivation(&frame);
       window.ConsumeFullscreenRequestToken();
     }
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 869b1fb..7231b5e 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -645,9 +645,6 @@
         PhysicalRect cursor_rect(cursor_offset, LayoutSize(size));
         if (!PhysicalRect(page->GetVisualViewport().VisibleContentRect())
                  .Contains(cursor_rect)) {
-          Deprecation::CountDeprecation(
-              node->GetExecutionContext(),
-              WebFeature::kCustomCursorIntersectsViewport);
           continue;
         }
       }
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
index d07131cd..0b40a08 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
@@ -456,10 +456,6 @@
       type = protocol::Audits::DeprecationIssueTypeEnum::
           CSSSelectorInternalMediaControlsOverlayCastButton;
       break;
-    case DeprecationIssueType::kCustomCursorIntersectsViewport:
-      type = protocol::Audits::DeprecationIssueTypeEnum::
-          CustomCursorIntersectsViewport;
-      break;
     case DeprecationIssueType::kDeprecationExample:
       type = protocol::Audits::DeprecationIssueTypeEnum::DeprecationExample;
       break;
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
index 7d2dbb2..221f57c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.h
@@ -48,7 +48,6 @@
   kCrossOriginWindowAlert,
   kCrossOriginWindowConfirm,
   kCSSSelectorInternalMediaControlsOverlayCastButton,
-  kCustomCursorIntersectsViewport,
   kDeprecationExample,
   kDocumentDomainSettingWithoutOriginAgentClusterHeader,
   kEventPath,
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 c442d61..b2adb237 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
@@ -606,6 +606,13 @@
     table_cell_column_index_ = table_cell_column_index;
   }
 
+  void SetTableSectionCollapsedBordersGeometry(
+      wtf_size_t start_row_index,
+      Vector<LayoutUnit>&& row_offsets) {
+    table_section_start_row_index_ = start_row_index;
+    table_section_row_offsets_ = std::move(row_offsets);
+  }
+
   void TransferGridLayoutData(
       std::unique_ptr<NGGridLayoutData> grid_layout_data) {
     grid_layout_data_ = std::move(grid_layout_data);
@@ -774,6 +781,8 @@
 
   // Table cell specific types.
   absl::optional<wtf_size_t> table_cell_column_index_;
+  wtf_size_t table_section_start_row_index_;
+  Vector<LayoutUnit> table_section_row_offsets_;
 
   NGBlockBreakTokenData* break_token_data_ = nullptr;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 96b0a7ca..f2452911 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -197,7 +197,8 @@
                        !builder->table_column_geometries_.IsEmpty() ||
                        builder->table_collapsed_borders_ ||
                        builder->table_collapsed_borders_geometry_ ||
-                       builder->table_cell_column_index_;
+                       builder->table_cell_column_index_ ||
+                       !builder->table_section_row_offsets_.IsEmpty();
 
   wtf_size_t num_fragment_items =
       builder->ItemsBuilder() ? builder->ItemsBuilder()->Size() : 0;
@@ -541,6 +542,10 @@
   }
   if (builder->table_cell_column_index_)
     table_cell_column_index = *builder->table_cell_column_index_;
+  if (!builder->table_section_row_offsets_.IsEmpty()) {
+    table_section_start_row_index = builder->table_section_start_row_index_;
+    table_section_row_offsets = std::move(builder->table_section_row_offsets_);
+  }
 }
 
 NGPhysicalBoxFragment::RareData::RareData(const RareData& other)
@@ -555,7 +560,9 @@
               ? new NGTableFragmentData::CollapsedBordersGeometry(
                     *other.table_collapsed_borders_geometry)
               : nullptr),
-      table_cell_column_index(other.table_cell_column_index) {}
+      table_cell_column_index(other.table_cell_column_index),
+      table_section_start_row_index(other.table_section_start_row_index),
+      table_section_row_offsets(other.table_section_row_offsets) {}
 
 const LayoutBox* NGPhysicalBoxFragment::OwnerLayoutBox() const {
   // TODO(layout-dev): We should probably get rid of this method, now that it
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index ecd0300..a5f3f53 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -160,6 +160,23 @@
     return ComputeRareDataAddress()->table_cell_column_index;
   }
 
+  absl::optional<wtf_size_t> TableSectionStartRowIndex() const {
+    DCHECK(IsTableNGSection());
+    if (!const_has_rare_data_)
+      return absl::nullopt;
+    const auto* rare_data = ComputeRareDataAddress();
+    if (rare_data->table_section_row_offsets.IsEmpty())
+      return absl::nullopt;
+    return rare_data->table_section_start_row_index;
+  }
+
+  const Vector<LayoutUnit>* TableSectionRowOffsets() const {
+    DCHECK(IsTableNGSection());
+    return const_has_rare_data_
+               ? &ComputeRareDataAddress()->table_section_row_offsets
+               : nullptr;
+  }
+
   // Returns the layout-overflow for this fragment.
   const PhysicalRect LayoutOverflow() const {
     if (is_legacy_layout_root_)
@@ -423,13 +440,19 @@
 
     const std::unique_ptr<const NGMathMLPaintInfo> mathml_paint_info;
 
-    // TablesNG rare data.
+    // Table rare-data.
     PhysicalRect table_grid_rect;
     NGTableFragmentData::ColumnGeometries table_column_geometries;
     scoped_refptr<const NGTableBorders> table_collapsed_borders;
     std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry>
         table_collapsed_borders_geometry;
+
+    // Table-cell rare-data.
     wtf_size_t table_cell_column_index;
+
+    // Table-section rare-data.
+    wtf_size_t table_section_start_row_index;
+    Vector<LayoutUnit> table_section_row_offsets;
   };
 
   const NGFragmentItems* ComputeItemsAddress() const {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
index 005ce0c..4b1d817 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
@@ -154,6 +154,11 @@
     const wtf_size_t new_start_row_index = new_section.start_row_index;
     const wtf_size_t old_start_row_index = old_section.start_row_index;
 
+    // Collapsed-border painting has a dependency on the row-index.
+    DCHECK_EQ(has_collapsed_borders, other.has_collapsed_borders);
+    if (has_collapsed_borders && new_start_row_index != old_start_row_index)
+      return false;
+
     const wtf_size_t new_end_row_index =
         new_start_row_index + new_section.row_count;
     const wtf_size_t old_end_row_index =
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
index b5193e1..dc6a370 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
@@ -41,20 +41,17 @@
 
   using ColumnGeometries = HeapVector<ColumnGeometry>;
 
-  // Column/row location is used for collapsed border painting.
-  // Only present if borders are collapsed.
+  // Column locations are used for collapsed-border painting.
   struct CollapsedBordersGeometry {
     USING_FAST_MALLOC(CollapsedBordersGeometry);
 
    public:
-    Vector<LayoutUnit> columns;  // Column offsets from table grid border.
-    Vector<LayoutUnit> rows;     // Row offsets from table grid border.
+    Vector<LayoutUnit> columns;
 
 #if DCHECK_IS_ON()
     void CheckSameForSimplifiedLayout(
         const CollapsedBordersGeometry& other) const {
       DCHECK(columns == other.columns);
-      DCHECK(rows == other.rows);
     }
 #endif
   };
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index d06bb7d4..2088efd 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -778,28 +778,20 @@
   }
   // Collapsed borders.
   if (!table_borders.IsEmpty()) {
-    LayoutUnit grid_inline_start = table_borders.TableBorder().inline_start;
     std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry>
         fragment_borders_geometry =
             std::make_unique<NGTableFragmentData::CollapsedBordersGeometry>();
-    for (const auto& column : column_locations) {
-      fragment_borders_geometry->columns.push_back(column.offset +
-                                                   grid_inline_start);
-    }
+    for (const auto& column : column_locations)
+      fragment_borders_geometry->columns.push_back(column.offset);
     DCHECK_NE(column_locations.size(), 0u);
     fragment_borders_geometry->columns.push_back(
-        column_locations.back().offset + column_locations.back().size +
-        grid_inline_start);
-    LayoutUnit row_offset = table_borders.TableBorder().block_start;
-    for (const auto& row : rows) {
-      fragment_borders_geometry->rows.push_back(row_offset);
-      row_offset += row.block_size;
-    }
-    fragment_borders_geometry->rows.push_back(row_offset);
-    // crbug.com/1179369 make sure dimensions of table_borders and
-    // fragment_borders_geometry are consistent.
+        column_locations.back().offset + column_locations.back().size);
+
+    // Ensure the dimensions of table_borders and fragment_borders_geometry are
+    // consistent.
     DCHECK_LE(table_borders.EdgesPerRow() / 2,
               fragment_borders_geometry->columns.size());
+
     container_builder_.SetTableCollapsedBorders(table_borders);
     container_builder_.SetTableCollapsedBordersGeometry(
         std::move(fragment_borders_geometry));
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
index bbc5df62..d2c1cd1 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
@@ -50,6 +50,9 @@
         offset.block_offset, FragmentainerSpaceAtBfcStart(ConstraintSpace()));
   };
 
+  Vector<LayoutUnit> row_offsets = {LayoutUnit()};
+  wtf_size_t actual_start_row_index = 0u;
+
   NGBlockChildIterator child_iterator(Node().FirstChild(), BreakToken(),
                                       /* calculate_child_idx */ true);
   for (auto entry = child_iterator.NextChild();
@@ -119,6 +122,13 @@
     offset.block_offset += fragment.BlockSize();
     is_first_non_collapsed_row &= is_row_collapsed;
 
+    if (table_data.has_collapsed_borders) {
+      // Determine the start row-index for this section.
+      if (row_offsets.size() == 1u)
+        actual_start_row_index = row_index;
+      row_offsets.emplace_back(offset.block_offset);
+    }
+
     if (container_builder_.HasInflowChildBreakInside())
       break;
   }
@@ -142,6 +152,12 @@
     container_builder_.SetBaseline(*section_baseline);
   container_builder_.SetIsTableNGPart();
 
+  // Store the collapsed-borders row geometry on this section fragment.
+  if (table_data.has_collapsed_borders && row_offsets.size() > 1u) {
+    container_builder_.SetTableSectionCollapsedBordersGeometry(
+        actual_start_row_index, std::move(row_offsets));
+  }
+
   if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
     NGBreakStatus status = FinishFragmentation(
         Node(), ConstraintSpace(), BorderPadding().block_end,
diff --git a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
index 3fdfc3c..dbe7c34 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_table_painters.cc
@@ -435,6 +435,31 @@
   }
 }
 
+namespace {
+
+bool IsFirstRowFragmented(const NGPhysicalBoxFragment& section) {
+  for (const auto& child : section.Children()) {
+    if (!child->IsTableNGRow())
+      continue;
+    return !To<NGPhysicalBoxFragment>(*child).IsFirstForNode();
+  }
+  return false;
+}
+
+bool IsLastRowFragmented(const NGPhysicalBoxFragment& section) {
+  const auto children = section.Children();
+  for (auto it = children.rbegin(); it != children.rend(); ++it) {
+    const auto& child = *it;
+    if (!child->IsTableNGRow())
+      continue;
+    return child->BreakToken() &&
+           !To<NGBlockBreakToken>(child->BreakToken())->IsAtBlockEnd();
+  }
+  return false;
+}
+
+}  // namespace
+
 void NGTablePainter::PaintCollapsedBorders(const PaintInfo& paint_info,
                                            const PhysicalOffset& paint_offset,
                                            const gfx::Rect& visual_rect) {
@@ -452,102 +477,165 @@
     return;
   DrawingRecorder recorder(paint_info.context, layout_table, paint_info.phase,
                            visual_rect);
-
-  PhysicalRect grid_paint_rect = fragment_.TableGridRect();
-  grid_paint_rect.offset += paint_offset;
-
-  WritingModeConverter grid_converter(fragment_.Style().GetWritingDirection(),
-                                      grid_paint_rect.size);
-
   AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
       fragment_.Style(), DarkModeFilter::ElementRole::kBackground));
 
-  for (NGTableCollapsedEdge edge = NGTableCollapsedEdge(*collapsed_borders, 0);
-       edge.Exists(); ++edge) {
-    if (!edge.CanPaint())
+  // We paint collapsed-borders section-by-section for fragmentation purposes.
+  // This means that we need to track the final row we've painted in each
+  // section to avoid double painting.
+  absl::optional<wtf_size_t> previous_painted_row_index;
+
+  for (const auto& child : fragment_.Children()) {
+    if (!child->IsTableNGSection())
       continue;
-    LayoutUnit inline_start;
-    LayoutUnit block_start;
-    LayoutUnit inline_size;
-    LayoutUnit block_size;
 
-    wtf_size_t table_row = edge.TableRow();
-    wtf_size_t table_column = edge.TableColumn();
-    if (edge.IsInlineAxis()) {
-      // crbug.com/1179369 This crash has been observed, but we have no
-      // reproducible case.
-      if (table_column + 1 >= collapsed_borders_geometry->columns.size()) {
-        NOTREACHED();
+    const auto& section = To<NGPhysicalBoxFragment>(*child);
+    const absl::optional<wtf_size_t> section_start_row_index =
+        section.TableSectionStartRowIndex();
+    if (!section_start_row_index)
+      continue;
+
+    bool is_first_row_fragmented = IsFirstRowFragmented(section);
+    bool is_last_row_fragmented = IsLastRowFragmented(section);
+
+    WritingModeConverter converter(fragment_.Style().GetWritingDirection(),
+                                   section.Size());
+
+    const auto& section_row_offsets = *section.TableSectionRowOffsets();
+    const wtf_size_t start_edge_index =
+        *section_start_row_index * collapsed_borders->EdgesPerRow();
+
+    for (NGTableCollapsedEdge edge =
+             NGTableCollapsedEdge(*collapsed_borders, start_edge_index);
+         edge.Exists(); ++edge) {
+      const wtf_size_t table_row = edge.TableRow();
+      const wtf_size_t table_column = edge.TableColumn();
+      const wtf_size_t fragment_table_row =
+          table_row - *section_start_row_index;
+
+      // Check if we've exhausted the rows in this section. Store the final row
+      // which we painted.
+      if (fragment_table_row >= section_row_offsets.size()) {
+        previous_painted_row_index = table_row - 1;
+        break;
+      }
+
+      if (!edge.CanPaint())
         continue;
-      }
-      inline_start = collapsed_borders_geometry->columns[table_column];
-      inline_size = collapsed_borders_geometry->columns[table_column + 1] -
-                    collapsed_borders_geometry->columns[table_column];
-      block_size = edge.BorderWidth();
-      CHECK_LT(table_row, collapsed_borders_geometry->rows.size());
-      block_start =
-          collapsed_borders_geometry->rows[table_row] - edge.BorderWidth() / 2;
-      LogicalSize start_joint;
-      LogicalSize end_joint;
-      bool start_wins;
-      bool end_wins;
-      ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
-                        start_wins, end_wins);
-      if (start_wins) {
-        inline_start -= start_joint.inline_size / 2;
-        inline_size += start_joint.inline_size / 2;
-      } else {
-        inline_start += start_joint.inline_size / 2;
-        inline_size -= start_joint.inline_size / 2;
-      }
-      if (end_wins) {
-        inline_size += end_joint.inline_size / 2;
-      } else {
-        inline_size -= end_joint.inline_size / 2;
-      }
-    } else {  // block_axis
-      CHECK_LT(table_row + 1, collapsed_borders_geometry->rows.size());
-      block_start = collapsed_borders_geometry->rows[table_row];
-      block_size =
-          collapsed_borders_geometry->rows[table_row + 1] - block_start;
-      CHECK_LT(table_column, collapsed_borders_geometry->columns.size());
-      inline_start = collapsed_borders_geometry->columns[table_column] -
-                     edge.BorderWidth() / 2;
-      inline_size = edge.BorderWidth();
-      LogicalSize start_joint;
-      LogicalSize end_joint;
-      bool start_wins;
-      bool end_wins;
-      ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
-                        start_wins, end_wins);
-      if (start_wins) {
-        block_start -= start_joint.block_size / 2;
-        block_size += start_joint.block_size / 2;
-      } else {
-        block_start += start_joint.block_size / 2;
-        block_size -= start_joint.block_size / 2;
-      }
-      if (end_wins) {
-        block_size += end_joint.block_size / 2;
-      } else {
-        block_size -= end_joint.block_size / 2;
-      }
-    }
-    const LogicalRect logical_border_rect(inline_start, block_start,
-                                          inline_size, block_size);
-    PhysicalRect physical_border_rect =
-        grid_converter.ToPhysical(logical_border_rect);
-    physical_border_rect.offset += grid_paint_rect.offset;
 
-    BoxSide box_side;
-    if (IsHorizontalWritingMode(fragment_.Style().GetWritingMode())) {
-      box_side = edge.IsInlineAxis() ? BoxSide::kTop : BoxSide::kLeft;
-    } else {
-      box_side = edge.IsInlineAxis() ? BoxSide::kLeft : BoxSide::kTop;
+      bool is_row_start_fragmented =
+          is_first_row_fragmented && fragment_table_row == 0u;
+
+      const LayoutUnit row_start_offset =
+          section_row_offsets[fragment_table_row];
+      const LayoutUnit column_start_offset =
+          collapsed_borders_geometry->columns[table_column];
+
+      LayoutUnit inline_start;
+      LayoutUnit block_start;
+      LayoutUnit inline_size;
+      LayoutUnit block_size;
+
+      if (edge.IsInlineAxis()) {
+        // NOTE: This crash has been observed, but we aren't able to find a
+        // reproducible testcase. See: crbug.com/1179369.
+        if (table_column + 1 >= collapsed_borders_geometry->columns.size()) {
+          NOTREACHED();
+          continue;
+        }
+
+        // Check if we have painted this inline border in a previous section.
+        if (previous_painted_row_index &&
+            *previous_painted_row_index == table_row) {
+          continue;
+        }
+
+        bool is_row_end_fragmented =
+            is_last_row_fragmented &&
+            fragment_table_row == section_row_offsets.size() - 1u;
+
+        // If the current row has been fragmented, omit the inline border.
+        if (is_row_start_fragmented || is_row_end_fragmented)
+          continue;
+
+        inline_start = column_start_offset;
+        inline_size = collapsed_borders_geometry->columns[table_column + 1] -
+                      column_start_offset;
+        block_size = edge.BorderWidth();
+        block_start = row_start_offset - edge.BorderWidth() / 2;
+        LogicalSize start_joint;
+        LogicalSize end_joint;
+        bool start_wins;
+        bool end_wins;
+        ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
+                          start_wins, end_wins);
+        if (start_wins) {
+          inline_start -= start_joint.inline_size / 2;
+          inline_size += start_joint.inline_size / 2;
+        } else {
+          inline_start += start_joint.inline_size / 2;
+          inline_size -= start_joint.inline_size / 2;
+        }
+        if (end_wins) {
+          inline_size += end_joint.inline_size / 2;
+        } else {
+          inline_size -= end_joint.inline_size / 2;
+        }
+      } else {  // block_axis
+        // Check if this block border exists in this section.
+        if (fragment_table_row + 1 >= section_row_offsets.size())
+          continue;
+
+        bool is_row_end_fragmented =
+            is_last_row_fragmented &&
+            fragment_table_row + 1u == section_row_offsets.size() - 1u;
+
+        block_start = row_start_offset;
+        block_size =
+            section_row_offsets[fragment_table_row + 1] - row_start_offset;
+        inline_start = column_start_offset - edge.BorderWidth() / 2;
+        inline_size = edge.BorderWidth();
+        LogicalSize start_joint;
+        LogicalSize end_joint;
+        bool start_wins;
+        bool end_wins;
+        ComputeEdgeJoints(*collapsed_borders, edge, start_joint, end_joint,
+                          start_wins, end_wins);
+        if (is_row_start_fragmented) {
+          // We don't need to perform any adjustment if we've been start
+          // fragmented as there isn't a joint here.
+        } else if (start_wins) {
+          block_start -= start_joint.block_size / 2;
+          block_size += start_joint.block_size / 2;
+        } else {
+          block_start += start_joint.block_size / 2;
+          block_size -= start_joint.block_size / 2;
+        }
+        if (is_row_end_fragmented) {
+          // We don't need to perform any adjustment if we've been end
+          // fragmented as there isn't a joint here.
+        } else if (end_wins) {
+          block_size += end_joint.block_size / 2;
+        } else {
+          block_size -= end_joint.block_size / 2;
+        }
+      }
+      const LogicalRect logical_border_rect(inline_start, block_start,
+                                            inline_size, block_size);
+      PhysicalRect physical_border_rect =
+          converter.ToPhysical(logical_border_rect);
+      physical_border_rect.offset += child.offset + paint_offset;
+
+      BoxSide box_side;
+      if (IsHorizontalWritingMode(fragment_.Style().GetWritingMode())) {
+        box_side = edge.IsInlineAxis() ? BoxSide::kTop : BoxSide::kLeft;
+      } else {
+        box_side = edge.IsInlineAxis() ? BoxSide::kLeft : BoxSide::kTop;
+      }
+      BoxBorderPainter::DrawBoxSide(
+          paint_info.context, ToPixelSnappedRect(physical_border_rect),
+          box_side, edge.BorderColor(), edge.BorderStyle(), auto_dark_mode);
     }
-    BoxBorderPainter::DrawBoxSide(
-        paint_info.context, ToPixelSnappedRect(physical_border_rect), box_side,
-        edge.BorderColor(), edge.BorderStyle(), auto_dark_mode);
   }
 }
 
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
index 420e6ff0..7ec8ee7 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
@@ -244,4 +244,7 @@
     const blink::FrameToken& child_frame_token,
     const WTF::String& srcdoc_value) {}
 
+void FakeLocalFrameHost::ReceivedDelegatedCapability(
+    blink::mojom::DelegatedCapability delegated_capability) {}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.h b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
index 56f5693..93aef56 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.h
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
@@ -163,6 +163,8 @@
       blink::mojom::PreferredColorScheme preferred_color_scheme) override;
   void DidChangeSrcDoc(const blink::FrameToken& child_frame_token,
                        const WTF::String& srcdoc_value) override;
+  void ReceivedDelegatedCapability(
+      blink::mojom::DelegatedCapability delegated_capability) override;
 
  private:
   void BindFrameHostReceiver(mojo::ScopedInterfaceEndpointHandle handle);
diff --git a/third_party/blink/renderer/extensions/chromeos/BUILD.gn b/third_party/blink/renderer/extensions/chromeos/BUILD.gn
index 3dc7524..c2bc0dbc 100644
--- a/third_party/blink/renderer/extensions/chromeos/BUILD.gn
+++ b/third_party/blink/renderer/extensions/chromeos/BUILD.gn
@@ -9,7 +9,10 @@
 
 assert(use_blink_extensions_chromeos)
 
-visibility = [ "//third_party/blink/renderer/extensions/*" ]
+visibility = [
+  "//third_party/blink/renderer/bindings/extensions/*",
+  "//third_party/blink/renderer/extensions/*",
+]
 
 config("extensions_chromeos_implementation") {
   visibility += [ "//third_party/blink/renderer/bindings/extensions/*" ]
@@ -42,6 +45,7 @@
   ]
 
   deps = [
+    ":chromeos_events",
     "//third_party/blink/renderer/core",
     "//third_party/blink/renderer/extensions/chromeos/system_extensions/hid",
     "//third_party/blink/renderer/extensions/chromeos/system_extensions/window_management",
@@ -51,3 +55,32 @@
   public_deps =
       [ "//third_party/blink/renderer/bindings/extensions/v8:v8_chromeos" ]
 }
+
+group("make_chromeos_generated") {
+  public_deps = [ ":make_chromeos_generated_event_target_names" ]
+}
+
+blink_chromeos_extensions_output_dir = "$blink_extensions_output_dir/chromeos"
+
+# make_names -------------------------------------------------------------------
+make_names("make_chromeos_generated_event_target_names") {
+  in_files = [ "event_target_chromeos_names.json5" ]
+  output_dir = blink_chromeos_extensions_output_dir
+}
+
+blink_extensions_chromeos_sources("chromeos_events") {
+  sources = [ "event_target_chromeos.h" ]
+  public_deps = [ ":chromeos_generated" ]
+}
+
+blink_extensions_chromeos_sources("chromeos_generated") {
+  # Targets from above that generate outputs that need to be compiled.
+  # All sources declared as outputs from these targets will be compiled
+  # into one target.
+  targets_generating_sources = [ ":make_chromeos_generated_event_target_names" ]
+
+  sources = []
+  foreach(current, targets_generating_sources) {
+    sources += get_target_outputs(current)
+  }
+}
diff --git a/third_party/blink/renderer/extensions/chromeos/chromeos_extensions.cc b/third_party/blink/renderer/extensions/chromeos/chromeos_extensions.cc
index 5b6d309..8957af2 100644
--- a/third_party/blink/renderer/extensions/chromeos/chromeos_extensions.cc
+++ b/third_party/blink/renderer/extensions/chromeos/chromeos_extensions.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/extensions_chromeos/v8/v8_chrome_os.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/extensions/chromeos/chromeos.h"
+#include "third_party/blink/renderer/extensions/chromeos/event_target_chromeos_names.h"
 #include "third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 #include "third_party/blink/renderer/platform/bindings/extensions_registry.h"
@@ -48,6 +49,15 @@
 void ChromeOSExtensions::Initialize() {
   ExtensionsRegistry::GetInstance().RegisterBlinkExtensionInstallCallback(
       &InstallChromeOSExtensions);
+
+  // Static strings need to be initialized here, before
+  // CoreInitializer::Initialize().
+  const unsigned kChromeOSStaticStringsCount =
+      event_target_names::kChromeOSNamesCount;
+  StringImpl::ReserveStaticStringsCapacityForSize(
+      kChromeOSStaticStringsCount + StringImpl::AllStaticStrings().size());
+
+  event_target_names::InitChromeOS();
 }
 
 void ChromeOSExtensions::InitServiceWorkerGlobalScope(
diff --git a/third_party/blink/renderer/extensions/chromeos/event_target_chromeos.h b/third_party/blink/renderer/extensions/chromeos/event_target_chromeos.h
new file mode 100644
index 0000000..6f886cb
--- /dev/null
+++ b/third_party/blink/renderer/extensions/chromeos/event_target_chromeos.h
@@ -0,0 +1,11 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_CHROMEOS_EVENT_TARGET_CHROMEOS_H_
+#define THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_CHROMEOS_EVENT_TARGET_CHROMEOS_H_
+
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/extensions/chromeos/event_target_chromeos_names.h"
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_EXTENSIONS_CHROMEOS_EVENT_TARGET_CHROMEOS_H_
diff --git a/third_party/blink/renderer/extensions/chromeos/event_target_chromeos_names.json5 b/third_party/blink/renderer/extensions/chromeos/event_target_chromeos_names.json5
new file mode 100644
index 0000000..694931ea
--- /dev/null
+++ b/third_party/blink/renderer/extensions/chromeos/event_target_chromeos_names.json5
@@ -0,0 +1,12 @@
+{
+  metadata: {
+    namespace: "event_target_names",
+    suffix: "ChromeOS",
+  },
+  # You don't need to specify ImplementedAs even though an interface name and
+  # its C++ class name don't match.  You need to specify ImplementedAs only if
+  # you'd like to change recorded names in ActivityLogger.
+  data: [
+    "CrosWindowManagement",
+  ]
+}
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/BUILD.gn b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/BUILD.gn
index 0f9fac3..35acf1c 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/BUILD.gn
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/BUILD.gn
@@ -14,6 +14,7 @@
 
   deps = [
     "//third_party/blink/renderer/bindings:generate_bindings_all",
+    "//third_party/blink/renderer/extensions/chromeos:chromeos_events",
     "//third_party/blink/renderer/platform",
     "//v8",
   ]
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
index 3e46007a4e..9c6264fd 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.cc
@@ -6,8 +6,10 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/extensions/chromeos/event_target_chromeos.h"
 #include "third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window.h"
 
 namespace blink {
@@ -53,10 +55,7 @@
 }
 
 const WTF::AtomicString& CrosWindowManagement::InterfaceName() const {
-  // TODO(b/221130654): Move to event_target_names::kCrosWindowManagement.
-  DEFINE_STATIC_LOCAL(const AtomicString, kInterfaceName,
-                      ("CrosWindowManagement"));
-  return kInterfaceName;
+  return event_target_names::kCrosWindowManagement;
 }
 
 ExecutionContext* CrosWindowManagement::GetExecutionContext() const {
@@ -102,6 +101,10 @@
   resolver->Resolve(results);
 }
 
+void CrosWindowManagement::DispatchStartEvent() {
+  DispatchEvent(*Event::Create(event_type_names::kStart));
+}
+
 void CrosWindowManagement::BindWindowManagerStartObserverImpl(
     mojo::PendingReceiver<mojom::blink::CrosWindowManagementStartObserver>
         receiver) {
diff --git a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
index 8dbc24f..2910fb4e 100644
--- a/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
+++ b/third_party/blink/renderer/extensions/chromeos/system_extensions/window_management/cros_window_management.h
@@ -50,6 +50,9 @@
   void WindowsCallback(ScriptPromiseResolver* resolver,
                        WTF::Vector<mojom::blink::CrosWindowInfoPtr> windows);
 
+  // mojom::blink::CrosWindowManagementObserver
+  void DispatchStartEvent() override;
+
  private:
   void BindWindowManagerStartObserverImpl(
       mojo::PendingReceiver<mojom::blink::CrosWindowManagementStartObserver>
diff --git a/third_party/blink/renderer/extensions/extensions.gni b/third_party/blink/renderer/extensions/extensions.gni
index 69b0dca..0a1c4c3 100644
--- a/third_party/blink/renderer/extensions/extensions.gni
+++ b/third_party/blink/renderer/extensions/extensions.gni
@@ -31,6 +31,7 @@
 
     deps = [
       "//third_party/blink/renderer/core",
+      "//third_party/blink/renderer/extensions/chromeos:make_chromeos_generated",
       "//third_party/blink/renderer/modules",
     ]
     if (defined(invoker.deps)) {
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
index 079d85a..307e408 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "third_party/blink/public/common/features.h"
@@ -554,8 +555,11 @@
     return;
   }
 
-  if (!custom_format_items_.IsEmpty() &&
-      !LocalFrame::HasTransientUserActivation(GetLocalFrame())) {
+  bool has_transient_user_activation =
+      LocalFrame::HasTransientUserActivation(GetLocalFrame());
+  base::UmaHistogramBoolean("Blink.Clipboard.HasTransientUserActivation",
+                            has_transient_user_activation);
+  if (!custom_format_items_.IsEmpty() && !has_transient_user_activation) {
     script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kSecurityError,
         "Must be handling a user gesture to use custom clipboard"));
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 817b3f2..73e4f0f3 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -168,7 +168,8 @@
   const unsigned kModulesStaticStringsCount =
       event_interface_names::kModulesNamesCount +
       event_target_names::kModulesNamesCount + indexed_db_names::kNamesCount;
-  StringImpl::ReserveStaticStringsCapacityForSize(kModulesStaticStringsCount);
+  StringImpl::ReserveStaticStringsCapacityForSize(
+      kModulesStaticStringsCount + StringImpl::AllStaticStrings().size());
 
   event_interface_names::InitModules();
   event_target_names::InitModules();
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.idl b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.idl
index 1a254308..c423fbf 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.idl
@@ -9,7 +9,7 @@
 [
     Exposed=Window
 ] interface RTCEncodedAudioFrame {
-    readonly attribute unsigned long long timestamp;  // RTP timestamp.
+    readonly attribute unsigned long timestamp;  // RTP timestamp.
     attribute ArrayBuffer data;
     RTCEncodedAudioFrameMetadata getMetadata();
     stringifier;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
index 257fa31..c3517d0 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.idl
@@ -16,7 +16,7 @@
     Exposed=Window
 ] interface RTCEncodedVideoFrame {
     readonly attribute RTCEncodedVideoFrameType type;
-    readonly attribute unsigned long long timestamp;  // RTP timestamp. 
+    readonly attribute unsigned long timestamp;  // RTP timestamp. 
     attribute ArrayBuffer data;
     RTCEncodedVideoFrameMetadata getMetadata();
     stringifier;
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_data_copy_to_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/audio_data_copy_to_fuzzer.cc
index e62d134..89660662 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_data_copy_to_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_data_copy_to_fuzzer.cc
@@ -26,6 +26,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   ScriptState* script_state =
       ToScriptStateForMainWorld(&page_holder->GetFrame());
   ScriptState::Scope scope(script_state);
@@ -48,15 +51,6 @@
   // is memory-backed.
   // TODO(chcunningham): Wait for promise resolution.
   audio_data->copyTo(destination, options, IGNORE_EXCEPTION_FOR_TESTING);
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder_fuzzer.cc
index 9e9fe0f..6fd6831 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder_fuzzer.cc
@@ -18,7 +18,6 @@
 #include "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -37,6 +36,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   //
   // NOTE: GC objects that need to survive iterations of the loop below
   // must be Persistent<>!
@@ -112,15 +114,6 @@
       }
     }
   }
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder_fuzzer.cc
index 93094b8..14856d8 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder_fuzzer.cc
@@ -27,7 +27,6 @@
 #include "third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -157,6 +156,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   // The platform audio encoder and some Image related classes that use
   // base::Singleton will expect this to exist for registering exit
   // callbacks (e.g. DarkModeImageClassifier).
@@ -255,15 +257,6 @@
       }
     }
   }
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
index f235e02af..1975a7ed 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <string>
 
+#include "base/callback_helpers.h"
 #include "media/base/limits.h"
 #include "media/base/sample_format.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
@@ -37,7 +38,9 @@
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -52,6 +55,20 @@
 
 }  // namespace
 
+base::ScopedClosureRunner MakeScopedGarbageCollectionRequest() {
+  return base::ScopedClosureRunner(WTF::Bind([]() {
+    // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
+    //
+    // Multiple GCs may be required to ensure everything is collected (due to
+    // a chain of persistent handles), so some objects may not be collected
+    // until a subsequent iteration. This is slow enough as is, so we compromise
+    // on one major GC, as opposed to the 5 used in V8GCController for unit
+    // tests.
+    V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
+        v8::Isolate::kFullGarbageCollection);
+  }));
+}
+
 FakeFunction::FakeFunction(std::string name) : name_(std::move(name)) {}
 
 ScriptValue FakeFunction::Call(ScriptState*, ScriptValue) {
diff --git a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
index 0c70b7d..d472582 100644
--- a/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
+++ b/third_party/blink/renderer/modules/webcodecs/fuzzer_utils.h
@@ -25,11 +25,17 @@
 
 #include <string>
 
+namespace base {
+class ScopedClosureRunner;
+}
+
 namespace blink {
 
 class DOMRectInit;
 class PlaneLayout;
 
+base::ScopedClosureRunner MakeScopedGarbageCollectionRequest();
+
 class FakeFunction : public ScriptFunction::Callable {
  public:
   explicit FakeFunction(std::string name);
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
index 52b726d3..cc11f6c 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_fuzzer.cc
@@ -24,7 +24,6 @@
 #include "third_party/blink/renderer/modules/webcodecs/image_track_list.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -99,6 +98,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(features::kJXL);
 
@@ -204,15 +206,8 @@
     }
   }
 
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
+  // Give other tasks a chance to run before we GC.
   base::RunLoop().RunUntilIdle();
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder_fuzzer.cc
index de3d474..75b31a9 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder_fuzzer.cc
@@ -18,7 +18,6 @@
 #include "third_party/blink/renderer/modules/webcodecs/video_decoder.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -37,6 +36,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   //
   // NOTE: GC objects that need to survive iterations of the loop below
   // must be Persistent<>!
@@ -112,15 +114,6 @@
       }
     }
   }
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder_fuzzer.cc
index 2be9804..d431fb06 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder_fuzzer.cc
@@ -19,7 +19,6 @@
 #include "third_party/blink/renderer/modules/webcodecs/video_encoder.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -40,6 +39,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   // Some Image related classes that use base::Singleton will expect this to
   // exist for registering exit callbacks (e.g. DarkModeImageClassifier).
   base::AtExitManager exit_manager;
@@ -130,15 +132,6 @@
       }
     }
   }
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame_copy_to_fuzzer.cc b/third_party/blink/renderer/modules/webcodecs/video_frame_copy_to_fuzzer.cc
index d088963..4580666 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame_copy_to_fuzzer.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame_copy_to_fuzzer.cc
@@ -28,6 +28,9 @@
     return page_holder.release();
   }();
 
+  // Request a full GC upon returning.
+  auto scoped_gc = MakeScopedGarbageCollectionRequest();
+
   ScriptState* script_state =
       ToScriptStateForMainWorld(&page_holder->GetFrame());
   ScriptState::Scope scope(script_state);
@@ -62,15 +65,6 @@
   // TODO(sandersd): Wait for promise resolution.
   video_frame->copyTo(script_state, destination, options,
                       IGNORE_EXCEPTION_FOR_TESTING);
-
-  // Request a V8 GC. Oilpan will be invoked by the GC epilogue.
-  //
-  // Multiple GCs may be required to ensure everything is collected (due to
-  // a chain of persistent handles), so some objects may not be collected until
-  // a subsequent iteration. This is slow enough as is, so we compromise on one
-  // major GC, as opposed to the 5 used in V8GCController for unit tests.
-  V8PerIsolateData::MainThreadIsolate()->RequestGarbageCollectionForTesting(
-      v8::Isolate::kFullGarbageCollection);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_family.cc b/third_party/blink/renderer/platform/fonts/font_family.cc
index feab496..36a9ba4 100644
--- a/third_party/blink/renderer/platform/fonts/font_family.cc
+++ b/third_party/blink/renderer/platform/fonts/font_family.cc
@@ -49,6 +49,14 @@
   return true;
 }
 
+wtf_size_t FontFamily::CountNames() const {
+  wtf_size_t count = 0;
+  for (const FontFamily* font_family = this; font_family;
+       font_family = font_family->Next())
+    ++count;
+  return count;
+}
+
 void FontFamily::AppendFamily(AtomicString family_name, Type family_type) {
   scoped_refptr<SharedFontFamily> appended_family = SharedFontFamily::Create();
   appended_family->SetFamily(family_name, family_type);
diff --git a/third_party/blink/renderer/platform/fonts/font_family.h b/third_party/blink/renderer/platform/fonts/font_family.h
index fa45a6d7..e71bfff 100644
--- a/third_party/blink/renderer/platform/fonts/font_family.h
+++ b/third_party/blink/renderer/platform/fonts/font_family.h
@@ -57,6 +57,9 @@
   const AtomicString& FamilyName() const { return family_name_; }
   bool FamilyIsGeneric() const { return family_type_ == Type::kGenericFamily; }
 
+  // Returns number of linked `FontFamily` including `this`, so return value is
+  // greater than or equal to 1. When `Next()` is `nullptr`, return value is 1.
+  wtf_size_t CountNames() const;
   const FontFamily* Next() const;
 
   void AppendFamily(scoped_refptr<SharedFontFamily>);
diff --git a/third_party/blink/renderer/platform/fonts/font_family_test.cc b/third_party/blink/renderer/platform/fonts/font_family_test.cc
index d8d4d91..98ef231 100644
--- a/third_party/blink/renderer/platform/fonts/font_family_test.cc
+++ b/third_party/blink/renderer/platform/fonts/font_family_test.cc
@@ -21,6 +21,27 @@
 
 }  // namespace
 
+TEST(FontFamilyTest, CountNames) {
+  {
+    FontFamily family;
+    EXPECT_EQ(1u, family.CountNames());
+  }
+  {
+    FontFamily family;
+    family.SetFamily("A", FontFamily::Type::kFamilyName);
+    CreateAndAppendFamily(family, "B", FontFamily::Type::kFamilyName);
+    EXPECT_EQ(2u, family.CountNames());
+  }
+  {
+    FontFamily family;
+    family.SetFamily("A", FontFamily::Type::kFamilyName);
+    FontFamily* b_family =
+        CreateAndAppendFamily(family, "B", FontFamily::Type::kFamilyName);
+    CreateAndAppendFamily(*b_family, "C", FontFamily::Type::kFamilyName);
+    EXPECT_EQ(3u, family.CountNames());
+  }
+}
+
 TEST(FontFamilyTest, ToString) {
   {
     FontFamily family;
diff --git a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
index b7a22ee..0a54857 100644
--- a/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
+++ b/third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.cc
@@ -5,8 +5,10 @@
 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_video_frame_pool.h"
 
 #include "components/viz/common/gpu/raster_context_provider.h"
+#include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "media/base/video_frame.h"
 #include "media/renderers/video_frame_rgba_to_yuva_converter.h"
@@ -139,22 +141,48 @@
     return false;
 #endif  // BUILDFLAG(IS_WIN)
 
-  scoped_refptr<media::VideoFrame> dst_frame =
-      pool_->MaybeCreateVideoFrame(src_size, dst_color_space);
+  auto dst_frame = pool_->MaybeCreateVideoFrame(src_size, dst_color_space);
   if (!dst_frame)
     return false;
 
-  gpu::SyncToken copy_done_sync_token;
+  auto* ri = raster_context_provider->RasterInterface();
+  DCHECK(ri);
+  unsigned query_id = 0;
+  ri->GenQueriesEXT(1, &query_id);
+  ri->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id);
+
   const bool copy_succeeded = media::CopyRGBATextureToVideoFrame(
       raster_context_provider, src_format, src_size, src_color_space,
-      src_surface_origin, src_mailbox_holder, dst_frame.get(),
-      copy_done_sync_token);
+      src_surface_origin, src_mailbox_holder, dst_frame.get());
   if (!copy_succeeded)
     return false;
 
   IgnoreResult(failure_runner.Release());
-  raster_context_provider->ContextSupport()->SignalSyncToken(
-      copy_done_sync_token, base::BindOnce(std::move(callback), dst_frame));
+  auto on_query_done_cb =
+      [](scoped_refptr<media::VideoFrame> frame,
+         base::WeakPtr<blink::WebGraphicsContext3DProviderWrapper> ctx_wrapper,
+         unsigned query_id, FrameReadyCallback callback) {
+        if (ctx_wrapper) {
+          if (auto* ctx_provider = ctx_wrapper->ContextProvider()) {
+            if (auto* ri_provider = ctx_provider->RasterContextProvider()) {
+              auto* ri = ri_provider->RasterInterface();
+              ri->DeleteQueriesEXT(1, &query_id);
+            }
+          }
+        }
+        std::move(callback).Run(std::move(frame));
+      };
+
+  // QueryEXT functions are used to make sure that CopyRGBATextureToVideoFrame()
+  // texture copy before we access GMB data.
+  ri->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+  auto* context_support = raster_context_provider->ContextSupport();
+  DCHECK(context_support);
+  context_support->SignalQuery(
+      query_id,
+      base::BindOnce(on_query_done_cb, dst_frame, weak_context_provider_,
+                     query_id, std::move(callback)));
+
   return true;
 }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/OWNERS b/third_party/blink/renderer/platform/image-decoders/avif/OWNERS
new file mode 100644
index 0000000..6be2ef7
--- /dev/null
+++ b/third_party/blink/renderer/platform/image-decoders/avif/OWNERS
@@ -0,0 +1,2 @@
+dalecurtis@chromium.org
+wtc@google.com
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index 8ff836d..ee6dbd0 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -1461,6 +1461,7 @@
       GetCurrentFrameFromCompositor();
   last_frame_request_time_ = tick_clock_->NowTicks();
   video_frame_readback_count_++;
+  pipeline_controller_->OnExternalVideoFrameRequest();
 
   video_renderer_.Paint(
       video_frame, canvas, gfx::RectF(rect), flags,
@@ -1471,6 +1472,7 @@
 scoped_refptr<media::VideoFrame> WebMediaPlayerImpl::GetCurrentFrame() {
   last_frame_request_time_ = tick_clock_->NowTicks();
   video_frame_readback_count_++;
+  pipeline_controller_->OnExternalVideoFrameRequest();
   return GetCurrentFrameFromCompositor();
 }
 
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
index 73bf7672..4e337d8a 100644
--- a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
+++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
@@ -262,11 +262,10 @@
     }
 
     if (dst_frame) {
-      gpu::SyncToken copy_done_sync_token;
       const bool copy_succeeded = media::CopyRGBATextureToVideoFrame(
           raster_context_provider.get(), format, source_frame->coded_size(),
           source_frame->ColorSpace(), origin, source_frame->mailbox_holder(0),
-          dst_frame.get(), copy_done_sync_token);
+          dst_frame.get());
       if (copy_succeeded) {
         // CopyRGBATextureToVideoFrame() operates on mailboxes and not frames,
         // so we must manually copy over properties relevant to the encoder.
@@ -284,8 +283,10 @@
         dst_frame->set_timestamp(source_frame->timestamp());
         dst_frame->set_metadata(source_frame->metadata());
 
-        // TODO(crbug.com/1224279): We should remove this wait by internalizing
-        // it into VideoFrame::Map().
+        // RI::Finish() makes sure that CopyRGBATextureToVideoFrame() finished
+        // texture copy before we call ConstructVideoFrameFromGpu(). It's not
+        // the best way to wait for completion, but it's the only sync way
+        // to wait, and making this function async is currently impractical.
         raster_context_provider->RasterInterface()->Finish();
         auto vf = ConstructVideoFrameFromGpu(std::move(dst_frame));
         return vf;
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 85e53a5..78fce2c 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,8 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-writing-modes/abs-pos-border-offset-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/abs-pos-border-offset-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/abs-pos-vlr-border-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
index ac5243e..6858146 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
+++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -44,6 +44,8 @@
 crbug.com/1209223 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
 crbug.com/626703 external/wpt/fetch/metadata/generated/css-images.https.sub.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/fetch/metadata/generated/css-images.sub.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/fetch/metadata/generated/element-link-icon.https.sub.html [ Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 98718f9..641a5d3 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1608,6 +1608,9 @@
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-table-cell-child.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-cell-expansion-003.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-row-paint-vlr-rtl.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-row-paint-vrl-rtl.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-section-paint-vrl-rtl.html [ Pass ]
@@ -3246,6 +3249,16 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux ] external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac10.15 ] external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac11 ] external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Win ] external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Linux ] virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac10.15 ] virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac11 ] virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Mac11-arm64 ] virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
+crbug.com/626703 [ Win ] virtual/wasm-csp/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html [ Timeout ]
 crbug.com/626703 [ Win11 ] external/wpt/mediacapture-streams/MediaDevices-enumerateDevices-per-origin-ids.sub.https.html [ Failure Timeout ]
 crbug.com/626703 [ Win11 ] external/wpt/navigation-timing/nav2_test_response_end_and_duration_before_during_and_after_load_event.html [ Timeout ]
 crbug.com/626703 [ Win11 ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaDevices-enumerateDevices-per-origin-ids.sub.https.html [ Failure Timeout ]
@@ -3875,6 +3888,9 @@
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-table-cell-child.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-cell-expansion-003.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-row-paint-vlr-rtl.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-row-paint-vrl-rtl.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-section-paint-vrl-rtl.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 0cc502b89..9472ce5 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: f84b1101de028a8a26acd28fba772f595e94454e
+Version: 5f3c4872a168e983f9310845ce739ef8e356584f
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 260fbd4..a42439d 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -143695,6 +143695,100 @@
        {}
       ]
      ],
+     "overflow-clip-margin-mul-column-border-box.html": [
+      "a0a1e895a66fa27a4ec95d115d646c0881e8da31",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-mul-column-border-box-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "overflow-clip-margin-mul-column-content-box.html": [
+      "26c081e04c8010dadc972dadcc74284a39e7cf8a",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-mul-column-content-box-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "overflow-clip-margin-mul-column-padding-box.html": [
+      "e3b3700608b4e1256481477db436d82bb3bf4528",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-mul-column-padding-box-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "overflow-clip-margin-visual-box-and-value-with-border-radius.html": [
+      "06ddb5c1630340d39af7cd2c35146910d5e5bcfc",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-visual-box-and-value-with-border-radius-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            5
+           ],
+           [
+            0,
+            100
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
+     "overflow-clip-margin-visual-box-and-value.html": [
+      "cfef43481446054671348ce5582438aaa10a7f3a",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-visual-box-and-value-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "overflow-clip-margin-visual-box.html": [
+      "db2c17fcc39cb057616a137d76b1fd12939d0127",
+      [
+       null,
+       [
+        [
+         "/css/css-overflow/overflow-clip-margin-visual-box-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "overflow-ellipsis-dynamic-001.html": [
       "2a9edba9308bf06009d7b9a27f21f1e0f1a231c7",
       [
@@ -209034,6 +209128,32 @@
      ]
     },
     "css-writing-modes": {
+     "abs-pos-border-offset-001.html": [
+      "f7b7375bdf085c7072e73bee190c7183a7adf701",
+      [
+       null,
+       [
+        [
+         "/css/css-writing-modes/abs-pos-border-offset-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "abs-pos-border-offset-002.html": [
+      "a954a567331dcbaaf43c3c61413dd2b113f207f3",
+      [
+       null,
+       [
+        [
+         "/css/css-writing-modes/abs-pos-border-offset-002-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "abs-pos-non-replaced-icb-vlr-003.xht": [
       "dea0f93355843cb66a111b01a4b0644060132e04",
       [
@@ -212375,6 +212495,32 @@
        {}
       ]
      ],
+     "abs-pos-vlr-border-001.html": [
+      "795bd972d452ce85d9a62c47ffa2e12133fed582",
+      [
+       null,
+       [
+        [
+         "/css/css-writing-modes/abs-pos-vlr-border-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "abs-pos-vlr-padding-001.html": [
+      "209cce14d2e1065a955f5403e1ff6053a0a7e67f",
+      [
+       null,
+       [
+        [
+         "/css/css-writing-modes/abs-pos-vlr-padding-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "abs-pos-with-replaced-child.html": [
       "c68de4fb57b32f810ab1668d98d27e0209428a0e",
       [
@@ -250382,7 +250528,17 @@
      "script-src-wasm-unsafe-eval-allows-wasm.any.js.headers": [
       "3463403572752de8ea928bd924378fe74b37aa52",
       []
-     ]
+     ],
+     "support": {
+      "iframe.html": [
+       "4d8b937558a9290c9ecc1de73c747ac9508d1507",
+       []
+      ],
+      "iframe.html.headers": [
+       "bc3a72a880db1bfc0ba930a2d663b666fe618656",
+       []
+      ]
+     }
     },
     "webrtc": {
      "webrtc.js": [
@@ -277641,6 +277797,30 @@
       "1ec2a5ce0a21c8dd578b3fcfde702307e4e2a9a8",
       []
      ],
+     "overflow-clip-margin-mul-column-border-box-ref.html": [
+      "98de614875ed30e0a99cb9771ee50ed749ade6b8",
+      []
+     ],
+     "overflow-clip-margin-mul-column-content-box-ref.html": [
+      "be5e46e341faa5475253704c3de092656ae4abce",
+      []
+     ],
+     "overflow-clip-margin-mul-column-padding-box-ref.html": [
+      "3cfa1a7eba3f724377530ec032c757029823e5e3",
+      []
+     ],
+     "overflow-clip-margin-visual-box-and-value-ref.html": [
+      "000e12ad1bf1d2ddb291d67adbb0d816c9edb822",
+      []
+     ],
+     "overflow-clip-margin-visual-box-and-value-with-border-radius-ref.html": [
+      "84b845fb5c74453801884661e4899c2666b9e112",
+      []
+     ],
+     "overflow-clip-margin-visual-box-ref.html": [
+      "5a265c7a73978fdf7e84fbfbf72e2593433cd264",
+      []
+     ],
      "overflow-scroll-big-border-small-content-ref.html": [
       "c7ea1807443ef1b2d454edd547e65c89a59cea16",
       []
@@ -289347,6 +289527,14 @@
       "53463b38f4154029f9fadf7c337119f9828f57b4",
       []
      ],
+     "abs-pos-border-offset-001-ref.html": [
+      "18a54a41b8b9b9fae5c8c72359a2f2a1cabf8a48",
+      []
+     ],
+     "abs-pos-border-offset-002-ref.html": [
+      "98ec0d0c708407179339ee48bec3cbae86777699",
+      []
+     ],
      "abs-pos-non-replaced-icb-vrl-004-ref.xht": [
       "8153a72aff210565ae77aaaaa2fc8cfe4dd03029",
       []
@@ -289407,6 +289595,14 @@
       "35f8d909c9cdae7e7df11b7ee25e792b212ea502",
       []
      ],
+     "abs-pos-vlr-border-001-ref.html": [
+      "e4098f1dd4574c65d3cbdf56e8d4dd1a0a15f448",
+      []
+     ],
+     "abs-pos-vlr-padding-001-ref.html": [
+      "6a941ff6407fc610681f674b67504492e8433e6d",
+      []
+     ],
      "astral-bidi": {
       "adlam-ref.html": [
        "d039127044e0bc9744bc358441da559cdd73bf81",
@@ -300045,10 +300241,6 @@
      []
     ],
     "anonymous-iframe": {
-     "anonymous-window.tentative.https.js": [
-      "14ea26dab5bcf069e8b4d1179b4a3754b0865bf6",
-      []
-     ],
      "require-corp-embed-anonymous-iframe.tentative.https.window.js.headers": [
       "6604450991a122e3e241e40b1b9e0516c525389d",
       []
@@ -304522,10 +304714,6 @@
       []
      ],
      "render-blocking": {
-      "header-inserted-preload-link.tentative.html.headers": [
-       "77399147c4c15310c2140752a0361e50730ef03e",
-       []
-      ],
       "support": {
        "dummy-1.js": [
         "597772cf641d83b41f0e4238a1fa74a050f29d3c",
@@ -304535,18 +304723,6 @@
         "9b85a21033e92389cf43e11f5fb97d6d4139f9d2",
         []
        ],
-       "font-with-preload-link-header.css": [
-        "ba0b24be2deaf9f34d4aab9ac2cf9961985866cb",
-        []
-       ],
-       "font-with-preload-link-header.css.headers": [
-        "77399147c4c15310c2140752a0361e50730ef03e",
-        []
-       ],
-       "subframe-render-blocking-preload.html": [
-        "31734b9bbc733de8db4e8b93070ceaf73b1823c2",
-        []
-       ],
        "target-red.css": [
         "a387acd4ecb17a0a0419fb5b3c90a1f0b2f1b0ea",
         []
@@ -363744,6 +363920,13 @@
        }
       ]
      ],
+     "postMessage-wasm-module.html": [
+      "9d5e1e0ff326f5607421706a01a6766b05a83a8d",
+      [
+       null,
+       {}
+      ]
+     ],
      "script-src-blocks-wasm.any.js": [
       "15e9d87ce9e0920472b4302f813df0ab0e79ed43",
       [
@@ -370240,6 +370423,13 @@
         {}
        ]
       ],
+      "container-units-svglength.html": [
+       "8bb227c0498941222275c024b2707fd089639624",
+       [
+        null,
+        {}
+       ]
+      ],
       "container-units-typed-om.html": [
        "6da3306fdfa3e88dde176d43c81813c3959a0306",
        [
@@ -379447,6 +379637,20 @@
         {}
        ]
       ],
+      "overflow-clip-margin-computed.html": [
+       "17f271b37034cdfe6f5b560a37473498bb672272",
+       [
+        null,
+        {}
+       ]
+      ],
+      "overflow-clip-margin.html": [
+       "bfa41285558f2bab20abefc80d535543056daea9",
+       [
+        null,
+        {}
+       ]
+      ],
       "overflow-computed.html": [
        "563d1b31d259c650935684b727544e75960b0444",
        [
@@ -390267,6 +390471,13 @@
          {}
         ]
        ],
+       "overflow-clip-margin.html": [
+        "d11964136450c3db199988d27a97b962c59bb751",
+        [
+         null,
+         {}
+        ]
+       ],
        "overflow-wrap.html": [
         "0a7bcc7ac303631b8b005fecf9e10b5e30a5c289",
         [
@@ -430429,7 +430640,7 @@
    "fullscreen": {
     "api": {
      "delegate-request.https.sub.tentative.html": [
-      "3760461d15dd41b778e9063d9f12d2250c4a6235",
+      "37e0099e6a8692ac24a531814cd3324c0b71e8d5",
       [
        null,
        {
@@ -431285,6 +431496,28 @@
        }
       ]
      ],
+     "anonymous-window.tentative.https.window.js": [
+      "a2ff8d8dbfb5988ebb53c2e24f1667315143b8f8",
+      [
+       "html/anonymous-iframe/anonymous-window.tentative.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/get-host-info.sub.js"
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/html/cross-origin-embedder-policy/credentialless/resources/common.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "cache-storage.tentative.https.window.js": [
       "8ce6410290a63bd234f3c8a2dd8a0e19c36c3300",
       [
@@ -431486,7 +431719,7 @@
       ]
      ],
      "initial-empty-document.tentative.https.window.js": [
-      "b4d4d5191a2985a5da4578552b9c04bfc2486234",
+      "4d9d00b55b2caabba4ea7953f855392e6f9304dd",
       [
        "html/anonymous-iframe/initial-empty-document.tentative.https.window.html",
        {
@@ -458333,20 +458566,6 @@
         {}
        ]
       ],
-      "header-inserted-preload-link.tentative.html": [
-       "12eda1de2cd4d01ec39221501ce5653c0350625b",
-       [
-        null,
-        {}
-       ]
-      ],
-      "invalid-render-blocking-preload-link.html": [
-       "a640f72e6feaf0288440a15a4ffb42ee50fdf59e",
-       [
-        null,
-        {}
-       ]
-      ],
       "non-render-blocking-scripts.optional.html": [
        "a4c32ea037b7b47490c54ddd7616b88bfebcdc76",
        [
@@ -458382,20 +458601,6 @@
         {}
        ]
       ],
-      "parser-inserted-modulepreload-link.tentative.html": [
-       "70fbac6d3ba18127f7040d2bc00b579a81c914e7",
-       [
-        null,
-        {}
-       ]
-      ],
-      "parser-inserted-preload-link.tentative.html": [
-       "827985d71a8a92e64a3bded1463e6d247d3d0f79",
-       [
-        null,
-        {}
-       ]
-      ],
       "parser-inserted-style-element.tentative.html": [
        "9a358aa4938e762b31b43fb38381a763c2222c33",
        [
@@ -458432,14 +458637,14 @@
        ]
       ],
       "remove-attr-unblocks-rendering.optional.html": [
-       "e7b75f5c139893606f1351c2f6f473263f4b3880",
+       "c73e3c6452b5acbc6546b182ccd94687aa7a2e22",
        [
         null,
         {}
        ]
       ],
       "remove-element-unblocks-rendering.optional.html": [
-       "8ab44cdb9d8bb5ddde06c4f5aa43aacafa103456",
+       "ad49c48c2e0d2332521ec660f5478b1a59dffa0c",
        [
         null,
         {}
@@ -458452,15 +458657,6 @@
         {}
        ]
       ],
-      "render-blocked-apis-by-preload-link.tentative.html": [
-       "07827371be7cacff976c2dc0ac893db977b16d38",
-       [
-        null,
-        {
-         "testdriver": true
-        }
-       ]
-      ],
       "script-inserted-module-script.tentative.html": [
        "73f0d3cdf4f2223530dcaf4e48fbf0f5034410cd",
        [
@@ -458468,20 +458664,6 @@
         {}
        ]
       ],
-      "script-inserted-modulepreload-link.tentative.html": [
-       "67c79f01bfec48ac795d4e8ef4ae1f8b9c4c4e98",
-       [
-        null,
-        {}
-       ]
-      ],
-      "script-inserted-preload-link.tentative.html": [
-       "a96e223d623cd89b2c907fed6d52f0b30552fedf",
-       [
-        null,
-        {}
-       ]
-      ],
       "script-inserted-script.html": [
        "faf346b4ddaa37a090b97225ef89ef2e96a9233b",
        [
@@ -458502,13 +458684,6 @@
         null,
         {}
        ]
-      ],
-      "stylesheet-header-inserted-preload-link.tentative.html": [
-       "6ad9ddb1cd1cdcee10fdad792f7dd0629c7f220d",
-       [
-        null,
-        {}
-       ]
       ]
      },
      "self-origin.any.js": [
@@ -511585,17 +511760,17 @@
     ]
    },
    "sanitizer-api": {
-    "element-set-sanitized-html.https.tentative.html": [
+    "element-set-sanitized-html.https.html": [
      "560e9cd63523287bacafa1b23a2ab3ea4bd21288",
      [
       null,
       {}
      ]
     ],
-    "idlharness.https.tentative.window.js": [
+    "idlharness.https.window.js": [
      "384317b8e55bd318464c68e20ca737bfb5b2c966",
      [
-      "sanitizer-api/idlharness.https.tentative.window.html",
+      "sanitizer-api/idlharness.https.window.html",
       {
        "script_metadata": [
         [
@@ -511610,21 +511785,21 @@
       }
      ]
     ],
-    "sanitizer-config.https.tentative.html": [
-     "fb29631e33ba184990b4e131e91171ba8a98ec89",
+    "sanitizer-config.https.html": [
+     "4faa156ead3b152f92d102c23e2a575837d293f4",
      [
       null,
       {}
      ]
     ],
-    "sanitizer-names.https.tentative.html": [
+    "sanitizer-names.https.html": [
      "22b913574e83f03f223bd49dff36573f908ab144",
      [
       null,
       {}
      ]
     ],
-    "sanitizer-query-config.https.tenative.html": [
+    "sanitizer-query-config.https.html": [
      "bd7c7ea3f65570e5b3e9e1dda3f098697ad22e8e",
      [
       null,
@@ -523420,7 +523595,7 @@
       ]
      ],
      "readable-stream.html": [
-      "59b57ce6723c10be746e2b3b478dcf81df105db1",
+      "b1ede4695bf4cd73a74fb43d52d471a72534d210",
       [
        null,
        {}
@@ -523447,8 +523622,15 @@
        {}
       ]
      ],
+     "transfer-with-messageport.window.js": [
+      "37f8c9df169607a4565f76d04c7cc56bc408af47",
+      [
+       "streams/transferable/transfer-with-messageport.window.html",
+       {}
+      ]
+     ],
      "transform-stream.html": [
-      "fbfbfe8fc1347ab9211845f439a9a7e105a33539",
+      "355d5d807433d75c02adff23e228d050b7772c0f",
       [
        null,
        {}
@@ -523469,7 +523651,7 @@
       ]
      ],
      "writable-stream.html": [
-      "adc6f457c27e87569be2fd32bfe723d29aef7135",
+      "297131b8a823538116e1a826f65b2ac814c6f65a",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html
new file mode 100644
index 0000000..9d5e1e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/postMessage-wasm-module.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>eval-in-iframe</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/common/utils.js"></script>
+</head>
+<body>
+    <iframe src="/content-security-policy/wasm-unsafe-eval/support/iframe.html">
+    </iframe>
+
+    <script>
+        async_test(t => {
+          self.addEventListener('message', t.step_func_done(({data}) => {
+            assert_equals(data.violatedDirective, "script-src");
+            assert_equals(data.originalPolicy, "default-src 'unsafe-inline'")
+            assert_equals(data.blockedURI, "wasm-eval")
+          }));
+        }, "Got the expected securitypolicyviolation in the iframe");
+
+        const iframe = document.querySelector('iframe');
+        iframe.addEventListener('load', () => {
+            let m = new WebAssembly.Module(
+                new Uint8Array([0, 0x61, 0x73, 0x6d, 0x1, 0, 0, 0]));
+            iframe.contentWindow.postMessage(m);
+        });
+    </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html
new file mode 100644
index 0000000..4d8b9375
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+  <h1>iframe</h1>
+  <script>
+    self.addEventListener('securitypolicyviolation', e => {
+      window.parent.postMessage({ violatedDirective: e.violatedDirective,
+      originalPolicy: e.originalPolicy, blockedURI: e.blockedURI });
+    });
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html.headers
new file mode 100644
index 0000000..bc3a72a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/wasm-unsafe-eval/support/iframe.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'unsafe-inline'
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr-ref.html
new file mode 100644
index 0000000..1841ff58
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <div style="background: dodgerblue; block-size: 120px;"></div>
+  <div style="block-size: 40px; border: solid 10px lime;"></div>
+  <div style="block-size: 135px; border: solid 10px; border-top: none;"></div>
+  <div style="block-size: 40px; border: solid 10px blue; border-top: none;"></div>
+  <div style="block-size: 15px; border: solid 10px blue; border-top: none;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html
new file mode 100644
index 0000000..60e1462
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-htb-ltr.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="match" href="table-collapsed-borders-paint-htb-ltr-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#fragmentation">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#rendering">
+<style>
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <table style="border-collapse: collapse; inline-size: 100%;">
+    <caption style="background: dodgerblue; block-size: 120px;"></caption>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid 10px lime;"></td>
+      </tr>
+      <tr style="block-size: 145px;">
+        <td style="border: solid 10px;"></td>
+      </tr>
+    </tbody>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+      <tr style="block-size: 25px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+    </tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl-ref.html
new file mode 100644
index 0000000..e17497a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+body {
+  writing-mode: vertical-lr;
+  direction: rtl;
+}
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <div style="background: dodgerblue; block-size: 120px;"></div>
+  <div style="block-size: 40px; border: solid 10px lime;"></div>
+  <div style="block-size: 135px; border: solid 10px; border-left: none;"></div>
+  <div style="block-size: 40px; border: solid 10px blue; border-left: none;"></div>
+  <div style="block-size: 15px; border: solid 10px blue; border-left: none;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html
new file mode 100644
index 0000000..d03968ec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vlr-rtl.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<link rel="match" href="table-collapsed-borders-paint-vlr-rtl-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#fragmentation">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#rendering">
+<style>
+body {
+  writing-mode: vertical-lr;
+  direction: rtl;
+}
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <table style="border-collapse: collapse; inline-size: 100%;">
+    <caption style="background: dodgerblue; block-size: 120px;"></caption>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid 10px lime;"></td>
+      </tr>
+      <tr style="block-size: 145px;">
+        <td style="border: solid 10px;"></td>
+      </tr>
+    </tbody>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+      <tr style="block-size: 25px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+    </tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr-ref.html
new file mode 100644
index 0000000..2721bc0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<style>
+body {
+  writing-mode: vertical-rl;
+}
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <div style="background: dodgerblue; block-size: 120px;"></div>
+  <div style="block-size: 40px; border: solid 10px lime;"></div>
+  <div style="block-size: 135px; border: solid 10px; border-right: none;"></div>
+  <div style="block-size: 40px; border: solid 10px blue; border-right: none;"></div>
+  <div style="block-size: 15px; border: solid 10px blue; border-right: none;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html
new file mode 100644
index 0000000..6dff501
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/table-collapsed-borders-paint-vrl-ltr.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<link rel="match" href="table-collapsed-borders-paint-vrl-ltr-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#fragmentation">
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#rendering">
+<style>
+body {
+  writing-mode: vertical-rl;
+}
+.multicol {
+  inline-size: 400px;
+  block-size: 100px;
+  columns: 4;
+  column-fill: auto;
+  gap: 10px;
+  padding: 10px;
+  border: solid 3px;
+}
+</style>
+<div class="multicol">
+  <table style="border-collapse: collapse; inline-size: 100%;">
+    <caption style="background: dodgerblue; block-size: 120px;"></caption>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid 10px lime;"></td>
+      </tr>
+      <tr style="block-size: 145px;">
+        <td style="border: solid 10px;"></td>
+      </tr>
+    </tbody>
+    <tbody>
+      <tr style="block-size: 50px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+      <tr style="block-size: 25px;">
+        <td style="border: solid blue 10px;"></td>
+      </tr>
+    </tbody>
+  </table>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/border-collapse-dynamic-section.html b/third_party/blink/web_tests/external/wpt/css/css-tables/border-collapse-dynamic-section.html
new file mode 100644
index 0000000..5f48749
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/border-collapse-dynamic-section.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<table style="border-collapse: collapse; width: 100px; background: red;">
+  <tbody>
+    <tr id="target" style="height: 50px; display: none;">
+      <td style="border: solid green 25px; background: green; padding: 0;"></td>
+    </tr>
+  </tbody>
+  <tbody>
+    <tr style="height: 25px;">
+      <td style="border: solid green 25px; padding: 0;"></td>
+    </tr>
+  </tbody>
+</table>
+<script>
+document.body.offsetTop;
+document.getElementById('target').style.display = '';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001-ref.html
new file mode 100644
index 0000000..18a54a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001-ref.html
@@ -0,0 +1,243 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<style>
+  body { margin: 0; }
+  .cb {
+    position: relative;
+    inline-size: 45px;
+    block-size: 40px;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    float: left;
+    margin-right: 5px;
+  }
+  .parent {
+    inline-size: 35px;
+    block-size: 30px;
+    background: orange;
+  }
+  .abspos {
+    inline-size: 20px;
+    block-size: 15px;
+    background: pink;
+  }
+
+  .vrl {
+    writing-mode: vertical-rl;
+  }
+  .vlr {
+    writing-mode: vertical-lr;
+  }
+  .htb {
+    writing-mode: horizontal-tb;
+  }
+
+  .ltr {
+    direction: ltr;
+  }
+  .rtl {
+    direction: rtl;
+  }
+
+  .sep {
+    clear: both;
+    display: block;
+    height: 5px;
+  }
+</style>
+<body>
+  <div class="cb htb ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb htb rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001.html
new file mode 100644
index 0000000..f7b7375
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-001.html
@@ -0,0 +1,248 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#block-flow">
+<meta name="assert"
+  content="This test checks that absolutely positioned elements are offset correctly in a bordered containing block, in different combinations of writing modes and directions." />
+<link rel="match" href="abs-pos-border-offset-001-ref.html">
+<style>
+  body { margin: 0; }
+  .cb {
+    position: relative;
+    inline-size: 45px;
+    block-size: 40px;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    float: left;
+    margin-right: 5px;
+  }
+  .parent {
+    inline-size: 35px;
+    block-size: 30px;
+    background: orange;
+  }
+  .abspos {
+    position: absolute;
+    inline-size: 20px;
+    block-size: 15px;
+    background: pink;
+  }
+
+  .vrl {
+    writing-mode: vertical-rl;
+  }
+  .vlr {
+    writing-mode: vertical-lr;
+  }
+  .htb {
+    writing-mode: horizontal-tb;
+  }
+
+  .ltr {
+    direction: ltr;
+  }
+  .rtl {
+    direction: rtl;
+  }
+
+  .sep {
+    clear: both;
+    display: block;
+    height: 5px;
+  }
+</style>
+<body>
+  <div class="cb htb ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb htb rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002-ref.html
new file mode 100644
index 0000000..98ec0d0c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002-ref.html
@@ -0,0 +1,397 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<style>
+  body { margin: 0; }
+  .cb {
+    position: relative;
+    inline-size: 45px;
+    block-size: 40px;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    float: left;
+    margin-right: 5px;
+  }
+  .parent {
+    inline-size: 35px;
+    block-size: 30px;
+    background: orange;
+  }
+  .abspos {
+    inline-size: 20px;
+    block-size: 15px;
+    background: pink;
+  }
+
+  .vrl {
+    writing-mode: vertical-rl;
+  }
+  .vlr {
+    writing-mode: vertical-lr;
+  }
+  .srl {
+    writing-mode: sideways-rl;
+  }
+  .slr {
+    writing-mode: sideways-lr;
+  }
+  .htb {
+    writing-mode: horizontal-tb;
+  }
+
+  .ltr {
+    direction: ltr;
+  }
+  .rtl {
+    direction: rtl;
+  }
+
+  .sep {
+    clear: both;
+    display: block;
+    height: 5px;
+  }
+</style>
+<body>
+  <div class="cb htb ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb htb rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb slr ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb slr rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb srl ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb srl rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002.html
new file mode 100644
index 0000000..a954a567
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-border-offset-002.html
@@ -0,0 +1,401 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#block-flow">
+<meta name="assert" content="This test checks that absolutely positioned elements are offset correctly in a bordered containing block, in different combinations of sideways writing modes and directions." />
+<link rel="match" href="abs-pos-border-offset-002-ref.html">
+<style>
+  body { margin: 0; }
+  .cb {
+    position: relative;
+    inline-size: 45px;
+    block-size: 40px;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    float: left;
+    margin-right: 5px;
+  }
+  .parent {
+    inline-size: 35px;
+    block-size: 30px;
+    background: orange;
+  }
+  .abspos {
+    position: absolute;
+    inline-size: 20px;
+    block-size: 15px;
+    background: pink;
+  }
+
+  .srl {
+    writing-mode: sideways-rl;
+  }
+  .slr {
+    writing-mode: sideways-lr;
+  }
+  .vrl {
+    writing-mode: vertical-rl;
+  }
+  .vlr {
+    writing-mode: vertical-lr;
+  }
+  .htb {
+    writing-mode: horizontal-tb;
+  }
+
+  .ltr {
+    direction: ltr;
+  }
+  .rtl {
+    direction: rtl;
+  }
+
+  .sep {
+    clear: both;
+    display: block;
+    height: 5px;
+  }
+</style>
+<body>
+  <div class="cb htb ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb htb rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb htb rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vlr rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vlr rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb vrl rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb vrl rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb slr ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb slr rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb slr rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb srl ltr">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl ltr">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+
+  <div class="cb srl rtl">
+    <div class="parent htb ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent htb rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vlr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vlr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vrl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent vrl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent slr ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent slr rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent srl ltr">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="cb srl rtl">
+    <div class="parent srl rtl">
+      <div class="abspos"></div>
+    </div>
+  </div>
+  <div class="sep"></div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001-ref.html
new file mode 100644
index 0000000..e4098f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<style>
+  body { margin: 0; }
+  .vert-cb {
+    position: relative;
+    width: 150px;
+    height: 60px;
+    writing-mode: vertical-lr;
+    direction: rtl;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    margin-bottom: 2px;
+  }
+  .horiz-parent {
+    width: 120px;
+    height: 100%;
+    box-sizing: border-box;
+    border: solid orange;
+    border-width: 4px 3px 2px 1px;
+    writing-mode: horizontal-tb;
+  }
+  .abspos-equivalent {
+    height: 40px;
+    background: pink;
+    border: solid black;
+    border-width: 2px 1px 4px 3px;
+  }
+</style>
+<body>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos-equivalent" style="width: max-content">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos-equivalent" style="width: 100px">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <canvas class="abspos-equivalent" height="40px" width="100px"></canvas>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <img src="broken" class="abspos-equivalent" height="40px" width="100px">
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001.html
new file mode 100644
index 0000000..795bd97
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-border-001.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#block-flow">
+<meta name="assert" content="This test checks that absolutely positioned elements are offset correctly in a containing block with an orthogonal writing mode and that has a border." />
+<link rel="match" href="abs-pos-vlr-border-001-ref.html">
+<style>
+  body { margin: 0; }
+  .vert-cb {
+    position: relative;
+    width: 150px;
+    height: 60px;
+    writing-mode: vertical-lr;
+    direction: rtl;
+    background: lightblue;
+    border: solid gray;
+    border-width: 1px 2px 3px 4px;
+    margin-bottom: 2px;
+  }
+  .horiz-parent {
+    width: 120px;
+    height: 100%;
+    box-sizing: border-box;
+    border: solid orange;
+    border-width: 4px 3px 2px 1px;
+    writing-mode: horizontal-tb;
+  }
+  .abspos {
+    position: absolute;
+    /* Specify a height to work around https://bugzilla.mozilla.org/1769102 */
+    height: 40px;
+    background: pink;
+    border: solid black;
+    border-width: 2px 1px 4px 3px;
+  }
+</style>
+<body>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos" style="width: max-content">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos" style="width: 100px">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <canvas class="abspos" height="40px" width="100px"></canvas>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <img src="broken" class="abspos" height="40px" width="100px">
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001-ref.html
new file mode 100644
index 0000000..6a941ff6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001-ref.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<style>
+  body { margin: 0; }
+  .vert-cb {
+    position: relative;
+    width: 150px;
+    height: 60px;
+    writing-mode: vertical-lr;
+    direction: rtl;
+    background: lightblue;
+    padding: 1px 2px 3px 4px;
+    margin-bottom: 2px;
+  }
+  .horiz-parent {
+    width: 120px;
+    height: 100%;
+    box-sizing: border-box;
+    border: solid orange;
+    border-width: 4px 3px 2px 1px;
+    writing-mode: horizontal-tb;
+  }
+  .abspos-equivalent {
+    height: 40px;
+    background: pink;
+    border: solid black;
+    border-width: 2px 1px 4px 3px;
+  }
+</style>
+<body>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos-equivalent" style="width: max-content">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos-equivalent" style="width: 100px">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <canvas class="abspos-equivalent" height="40px" width="100px"></canvas>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <img src="broken" class="abspos-equivalent" height="40px" width="100px">
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001.html
new file mode 100644
index 0000000..209cce1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-vlr-padding-001.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#block-flow">
+<meta name="assert" content="This test checks that absolutely positioned elements are offset correctly in a containing block with a different writing mode and that has padding." />
+<link rel="match" href="abs-pos-vlr-padding-001-ref.html">
+<style>
+  body { margin: 0; }
+  .vert-cb {
+    position: relative;
+    width: 150px;
+    height: 60px;
+    writing-mode: vertical-lr;
+    direction: rtl;
+    background: lightblue;
+    padding: 1px 2px 3px 4px;
+    margin-bottom: 2px;
+  }
+  .horiz-parent {
+    width: 120px;
+    height: 100%;
+    box-sizing: border-box;
+    border: solid orange;
+    border-width: 4px 3px 2px 1px;
+    writing-mode: horizontal-tb;
+  }
+  .abspos {
+    position: absolute;
+    /* Specify a height to work around https://bugzilla.mozilla.org/1769102 */
+    height: 40px;
+    background: pink;
+    border: solid black;
+    border-width: 2px 1px 4px 3px;
+  }
+</style>
+<body>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos" style="width: max-content">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <div class="abspos" style="width: 100px">Hello</div>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <canvas class="abspos" height="40px" width="100px"></canvas>
+    </div>
+  </div>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <img src="broken" class="abspos" height="40px" width="100px">
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.https.sub.html b/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.https.sub.html
index a0f683a..988b07c 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.https.sub.html
@@ -7,6 +7,7 @@
 -->
 <html lang="en">
   <meta charset="utf-8">
+  <meta name="timeout" content="long">
   <title>HTTP headers on request for HTML form navigation</title>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.sub.html b/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.sub.html
index c4a5e2f..f862062 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/generated/form-submission.sub.html
@@ -7,6 +7,7 @@
 -->
 <html lang="en">
   <meta charset="utf-8">
+  <meta name="timeout" content="long">
   <title>HTTP headers on request for HTML form navigation</title>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/tools/templates/form-submission.sub.html b/third_party/blink/web_tests/external/wpt/fetch/metadata/tools/templates/form-submission.sub.html
index cbf7b5ca..4c9c8c5 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/metadata/tools/templates/form-submission.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/tools/templates/form-submission.sub.html
@@ -4,6 +4,7 @@
 -->
 <html lang="en">
   <meta charset="utf-8">
+  <meta name="timeout" content="long">
   <title>HTTP headers on request for HTML form navigation</title>
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html b/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html
index 3760461d15..37e0099 100644
--- a/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html
@@ -14,6 +14,11 @@
   Verifies that element.requestFullscreen() call from a cross-origin subframe without user
   activation works if and only if the top frame has user activation and it delegates the capability
   to the subframe.
+
+  https://wicg.github.io/capability-delegation/spec.html
+
+  See wpt/html/user-activation/propagation*.html for child->parent user activation visibility tests.
+  TODO: Check same-origin iframes, sibling frames, and popup<->opener delegation.
 </div>
 
 <iframe allow="fullscreen" width="300px" height="50px"
diff --git a/third_party/blink/web_tests/external/wpt/streams/transferable/readable-stream.html b/third_party/blink/web_tests/external/wpt/streams/transferable/readable-stream.html
index 59b57ce6..b1ede46 100644
--- a/third_party/blink/web_tests/external/wpt/streams/transferable/readable-stream.html
+++ b/third_party/blink/web_tests/external/wpt/streams/transferable/readable-stream.html
@@ -135,18 +135,7 @@
   assert_array_equals(rs.events, ['pull'], 'pull() should have been called');
 }, 'the extra queue from transferring is counted in chunks');
 
-promise_test(async () => {
-  const rs = await recordingTransferredReadableStream();
-  rs.cancel('message');
-  await delay(0);
-  assert_array_equals(rs.events, ['pull', 'cancel', 'message'],
-                      'cancel() should have been called');
-  const reader = rs.getReader();
-  // Check the stream really got closed.
-  await reader.closed;
-}, 'cancel should be propagated to the original');
-
-promise_test(async () => {
+async function transferredReadableStreamWithCancelPromise() {
   let resolveCancelCalled;
   const cancelCalled = new Promise(resolve => {
     resolveCancelCalled = resolve;
@@ -156,6 +145,22 @@
       resolveCancelCalled();
     }
   });
+  return { rs, cancelCalled };
+}
+
+promise_test(async () => {
+  const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise();
+  rs.cancel('message');
+  await cancelCalled;
+  assert_array_equals(rs.events, ['pull', 'cancel', 'message'],
+                      'cancel() should have been called');
+  const reader = rs.getReader();
+  // Check the stream really got closed.
+  await reader.closed;
+}, 'cancel should be propagated to the original');
+
+promise_test(async () => {
+  const { rs, cancelCalled } = await transferredReadableStreamWithCancelPromise();
   const reader = rs.getReader();
   const readPromise = reader.read();
   reader.cancel('done');
diff --git a/third_party/blink/web_tests/external/wpt/streams/transferable/transfer-with-messageport.window.js b/third_party/blink/web_tests/external/wpt/streams/transferable/transfer-with-messageport.window.js
new file mode 100644
index 0000000..37f8c9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/transferable/transfer-with-messageport.window.js
@@ -0,0 +1,219 @@
+"use strict";
+
+function receiveEventOnce(target, name) {
+  return new Promise(resolve => {
+    target.addEventListener(
+      name,
+      ev => {
+        resolve(ev);
+      },
+      { once: true }
+    );
+  });
+}
+
+async function postAndTestMessageEvent(data, transfer, title) {
+  postMessage(data, "*", transfer);
+  const messagePortCount = transfer.filter(i => i instanceof MessagePort)
+    .length;
+  const ev = await receiveEventOnce(window, "message");
+  assert_equals(
+    ev.ports.length,
+    messagePortCount,
+    `Correct number of ports ${title}`
+  );
+  for (const [i, port] of ev.ports.entries()) {
+    assert_true(
+      port instanceof MessagePort,
+      `ports[${i}] include MessagePort ${title}`
+    );
+  }
+  for (const [key, value] of Object.entries(data)) {
+    assert_true(
+      ev.data[key] instanceof value.constructor,
+      `data.${key} has correct interface ${value.constructor.name} ${title}`
+    );
+  }
+}
+
+async function transferMessagePortWithOrder1(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { stream, port2: channel.port2 },
+    [stream, channel.port2],
+    `when transferring [${stream.constructor.name}, MessagePort]`
+  );
+}
+
+async function transferMessagePortWithOrder2(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { stream, port2: channel.port2 },
+    [channel.port2, stream],
+    `when transferring [MessagePort, ${stream.constructor.name}]`
+  );
+}
+
+async function transferMessagePortWithOrder3(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { port1: channel.port1, stream, port2: channel.port2 },
+    [channel.port1, stream, channel.port2],
+    `when transferring [MessagePort, ${stream.constructor.name}, MessagePort]`
+  );
+}
+
+async function transferMessagePortWithOrder4(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    {},
+    [channel.port1, stream, channel.port2],
+    `when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with empty data`
+  );
+}
+
+async function transferMessagePortWithOrder5(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { port2: channel.port2, port1: channel.port1, stream },
+    [channel.port1, stream, channel.port2],
+    `when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with data having different order`
+  );
+}
+
+async function transferMessagePortWithOrder6(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { port2: channel.port2, port1: channel.port1 },
+    [channel.port1, stream, channel.port2],
+    `when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with stream not being in the data`
+  );
+}
+
+async function transferMessagePortWithOrder7(stream) {
+  const channel = new MessageChannel();
+  await postAndTestMessageEvent(
+    { stream },
+    [channel.port1, stream, channel.port2],
+    `when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with ports not being in the data`
+  );
+}
+
+async function transferMessagePortWith(constructor) {
+  await transferMessagePortWithOrder1(new constructor());
+  await transferMessagePortWithOrder2(new constructor());
+  await transferMessagePortWithOrder3(new constructor());
+}
+
+async function advancedTransferMesagePortWith(constructor) {
+  await transferMessagePortWithOrder4(new constructor());
+  await transferMessagePortWithOrder5(new constructor());
+  await transferMessagePortWithOrder6(new constructor());
+  await transferMessagePortWithOrder7(new constructor());
+}
+
+async function mixedTransferMessagePortWithOrder1() {
+  const channel = new MessageChannel();
+  const readable = new ReadableStream();
+  const writable = new WritableStream();
+  const transform = new TransformStream();
+  await postAndTestMessageEvent(
+    {
+      readable,
+      writable,
+      transform,
+      port1: channel.port1,
+      port2: channel.port2,
+    },
+    [readable, writable, transform, channel.port1, channel.port2],
+    `when transferring [ReadableStream, WritableStream, TransformStream, MessagePort, MessagePort]`
+  );
+}
+
+async function mixedTransferMessagePortWithOrder2() {
+  const channel = new MessageChannel();
+  const readable = new ReadableStream();
+  const writable = new WritableStream();
+  const transform = new TransformStream();
+  await postAndTestMessageEvent(
+    { readable, writable, transform },
+    [transform, channel.port1, readable, channel.port2, writable],
+    `when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream]`
+  );
+}
+
+async function mixedTransferMessagePortWithOrder3() {
+  const channel = new MessageChannel();
+  const readable1 = new ReadableStream();
+  const readable2 = new ReadableStream();
+  const writable1 = new WritableStream();
+  const writable2 = new WritableStream();
+  const transform1 = new TransformStream();
+  const transform2 = new TransformStream();
+  await postAndTestMessageEvent(
+    { readable1, writable1, transform1, readable2, writable2, transform2 },
+    [
+      transform2,
+      channel.port1,
+      readable1,
+      channel.port2,
+      writable2,
+      readable2,
+      writable1,
+      transform1,
+    ],
+    `when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream, ReadableStream, WritableStream, TransformStream] but with the data having different order`
+  );
+}
+
+async function mixedTransferMesagePortWith() {
+  await mixedTransferMessagePortWithOrder1();
+  await mixedTransferMessagePortWithOrder2();
+  await mixedTransferMessagePortWithOrder3();
+}
+
+promise_test(async t => {
+  await transferMessagePortWith(ReadableStream);
+}, "Transferring a MessagePort with a ReadableStream should set `.ports`");
+
+promise_test(async t => {
+  await transferMessagePortWith(WritableStream);
+}, "Transferring a MessagePort with a WritableStream should set `.ports`");
+
+promise_test(async t => {
+  await transferMessagePortWith(TransformStream);
+}, "Transferring a MessagePort with a TransformStream should set `.ports`");
+
+promise_test(async t => {
+  await transferMessagePortWith(ReadableStream);
+}, "Transferring a MessagePort with a ReadableStream should set `.ports`, advanced");
+
+promise_test(async t => {
+  await transferMessagePortWith(WritableStream);
+}, "Transferring a MessagePort with a WritableStream should set `.ports`, advanced");
+
+promise_test(async t => {
+  await transferMessagePortWith(TransformStream);
+}, "Transferring a MessagePort with a TransformStream should set `.ports`, advanced");
+
+promise_test(async t => {
+  await mixedTransferMesagePortWith();
+}, "Transferring a MessagePort with multiple streams should set `.ports`");
+
+test(() => {
+  assert_throws_dom("DataCloneError", () =>
+    postMessage({ stream: new ReadableStream() }, "*")
+  );
+}, "ReadableStream must not be serializable");
+
+test(() => {
+  assert_throws_dom("DataCloneError", () =>
+    postMessage({ stream: new WritableStream() }, "*")
+  );
+}, "WritableStream must not be serializable");
+
+test(() => {
+  assert_throws_dom("DataCloneError", () =>
+    postMessage({ stream: new TransformStream() }, "*")
+  );
+}, "TransformStream must not be serializable");
diff --git a/third_party/blink/web_tests/external/wpt/streams/transferable/transform-stream.html b/third_party/blink/web_tests/external/wpt/streams/transferable/transform-stream.html
index fbfbfe8f..355d5d80 100644
--- a/third_party/blink/web_tests/external/wpt/streams/transferable/transform-stream.html
+++ b/third_party/blink/web_tests/external/wpt/streams/transferable/transform-stream.html
@@ -66,9 +66,14 @@
       controller.close();
     }
   });
+  let resolve;
+  const ready = new Promise(r => resolve = r);
   let result = '';
   const sink = new WritableStream({
     write(chunk) {
+      if (result) {
+        resolve();
+      }
       result += chunk;
     }
   });
@@ -93,8 +98,7 @@
   });
   postMessage({source, sink, transform1, transform2}, '*',
               [source, transform1, sink, transform2]);
-  return promise
-    .then(() => delay(0))
+  return ready
     .then(() => {
       assert_equals(result, 'HELLO HELLO THERE THERE ',
                     'transforms should have been applied');
diff --git a/third_party/blink/web_tests/external/wpt/streams/transferable/writable-stream.html b/third_party/blink/web_tests/external/wpt/streams/transferable/writable-stream.html
index adc6f45..297131b 100644
--- a/third_party/blink/web_tests/external/wpt/streams/transferable/writable-stream.html
+++ b/third_party/blink/web_tests/external/wpt/streams/transferable/writable-stream.html
@@ -99,35 +99,46 @@
   const writer = transferred.getWriter();
   await writer.write('a');
   let writeDone = false;
-  writer.write('b').then(() => {
+  const writePromise = writer.write('b').then(() => {
     writeDone = true;
   });
   await flushAsyncEvents();
   assert_false(writeDone, 'second write should not have resolved yet');
   resolveWrite();
-  await delay(0);
-  assert_true(writeDone, 'second write should have resolved');
+  await writePromise; // (makes sure this doesn't cause timeout)
 }, 'second write should wait for first underlying write to complete');
 
-promise_test(async t => {
-  const orig = recordingWritableStream();
+async function transferredWritableStreamWithAbortPromise() {
+  let resolveAbortCalled;
+  const abortCalled = new Promise(resolve => {
+    resolveAbortCalled = resolve;
+  });
+  const orig = recordingWritableStream({
+    abort() {
+      resolveAbortCalled();
+    }
+  });
   const transferred = await transfer(orig);
+  return { orig, transferred, abortCalled };
+}
+
+promise_test(async t => {
+  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
   transferred.abort('p');
-  await delay(0);
+  await abortCalled;
   assert_array_equals(orig.events, ['abort', 'p'],
                       'abort() should have been called');
 }, 'abort() should work');
 
 promise_test(async t => {
-  const orig = recordingWritableStream();
-  const transferred = await transfer(orig);
+  const { orig, transferred, abortCalled } = await transferredWritableStreamWithAbortPromise();
   const writer = transferred.getWriter();
   // A WritableStream object cannot be cloned.
   await promise_rejects_dom(t, 'DataCloneError', writer.write(new WritableStream()),
                             'the write should reject');
   await promise_rejects_dom(t, 'DataCloneError', writer.closed,
                             'the stream should be errored');
-  await delay(0);
+  await abortCalled;
   assert_equals(orig.events.length, 2, 'abort should have been called');
   assert_equals(orig.events[0], 'abort', 'first event should be abort');
   assert_equals(orig.events[1].name, 'DataCloneError',
diff --git a/third_party/ipcz/src/BUILD.gn b/third_party/ipcz/src/BUILD.gn
index 13dc9c5..9f214d0 100644
--- a/third_party/ipcz/src/BUILD.gn
+++ b/third_party/ipcz/src/BUILD.gn
@@ -5,6 +5,10 @@
 import("//build_overrides/ipcz.gni")
 import("//testing/test.gni")
 
+# ipcz will not implement a multiprocess reference driver or its supporting
+# primitives for iOS or NaCl platforms. Note that this only affects ipcz tests.
+enable_ipcz_multiprocess_test_support = !is_ios && !is_nacl
+
 shared_library("ipcz_shared") {
   output_name = "ipcz"
   sources = [
@@ -126,6 +130,8 @@
 }
 
 ipcz_source_set("reference_drivers") {
+  testonly = true
+
   public = [ "reference_drivers/single_process_reference_driver.h" ]
 
   sources = [
@@ -136,9 +142,58 @@
     "reference_drivers/single_process_reference_driver.cc",
   ]
 
+  if (enable_ipcz_multiprocess_test_support) {
+    public += [
+      "reference_drivers/memory.h",
+      "reference_drivers/os_handle.h",
+    ]
+    sources += [ "reference_drivers/memory.cc" ]
+
+    if (is_android) {
+      sources += [
+        "reference_drivers/memory_android.cc",
+        "reference_drivers/os_handle_posix.cc",
+        "reference_drivers/os_handle_posix.h",
+      ]
+    } else if (is_mac) {
+      sources += [
+        "reference_drivers/memory_mac.cc",
+        "reference_drivers/os_handle_mac.cc",
+        "reference_drivers/os_handle_mac.h",
+      ]
+    } else if (is_win) {
+      sources += [
+        "reference_drivers/memory_win.cc",
+        "reference_drivers/os_handle_win.cc",
+        "reference_drivers/os_handle_win.h",
+      ]
+    } else if (is_fuchsia) {
+      sources += [
+        "reference_drivers/memory_fuchsia.cc",
+        "reference_drivers/os_handle_fuchsia.cc",
+        "reference_drivers/os_handle_fuchsia.h",
+      ]
+    } else if (is_posix) {
+      sources += [
+        "reference_drivers/memory_posix.cc",
+        "reference_drivers/os_handle_posix.cc",
+        "reference_drivers/os_handle_posix.h",
+      ]
+    }
+  }
+
   ipcz_deps = [ ":util" ]
   public_deps = [ ":ipcz_header" ]
   configs = [ ":ipcz_include_src_dir" ]
+
+  deps = []
+  if (is_fuchsia) {
+    public_deps += [ "//third_party/fuchsia-sdk/sdk/pkg/zx" ]
+  }
+
+  if (is_android) {
+    deps += [ "//third_party/ashmem" ]
+  }
 }
 
 ipcz_source_set("util") {
@@ -295,6 +350,10 @@
     "util/stack_trace_test.cc",
   ]
 
+  if (enable_ipcz_multiprocess_test_support) {
+    sources += [ "reference_drivers/memory_test.cc" ]
+  }
+
   deps = [
     "//testing/gmock",
     "//testing/gtest",
diff --git a/third_party/ipcz/src/reference_drivers/memory.cc b/third_party/ipcz/src/reference_drivers/memory.cc
new file mode 100644
index 0000000..041fa84
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <utility>
+
+#include "build/build_config.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+Memory::Mapping::Mapping() = default;
+
+Memory::Mapping::Mapping(void* base_address, size_t size)
+    : base_address_(base_address), size_(size) {}
+
+Memory::Mapping::Mapping(Mapping&& other)
+    : base_address_(std::exchange(other.base_address_, nullptr)),
+      size_(std::exchange(other.size_, 0)) {}
+
+Memory::Mapping& Memory::Mapping::operator=(Mapping&& other) {
+  Reset();
+  base_address_ = std::exchange(other.base_address_, nullptr);
+  size_ = std::exchange(other.size_, 0);
+  return *this;
+}
+
+Memory::Mapping::~Mapping() {
+  Reset();
+}
+
+Memory::Memory() = default;
+
+Memory::Memory(OSHandle handle, size_t size)
+    : handle_(std::move(handle)), size_(size) {}
+
+Memory::Memory(Memory&&) = default;
+
+Memory& Memory::operator=(Memory&&) = default;
+
+Memory::~Memory() = default;
+
+Memory Memory::Clone() {
+  ABSL_ASSERT(is_valid());
+  return Memory(handle_.Clone(), size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory.h b/third_party/ipcz/src/reference_drivers/memory.h
new file mode 100644
index 0000000..eaf24857
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory.h
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+
+namespace ipcz::reference_drivers {
+
+// Cross-platform abstraction for a shared memory region.
+class Memory {
+ public:
+  // Cross-platform abstraction for an active mapping of a shared memory region.
+  //
+  // Instances of this object should be acquired from Memory::Map().
+  class Mapping {
+   public:
+    Mapping();
+    Mapping(void* base_address, size_t size);
+    Mapping(Mapping&&);
+    Mapping& operator=(Mapping&&);
+    Mapping(const Mapping&) = delete;
+    Mapping& operator=(const Mapping&) = delete;
+    ~Mapping();
+
+    bool is_valid() const { return base_address_ != nullptr; }
+
+    size_t size() const { return size_; }
+    void* base() const { return base_address_; }
+
+    absl::Span<uint8_t> bytes() const {
+      return {static_cast<uint8_t*>(base()), size_};
+    }
+
+    template <typename T>
+    T* As() const {
+      return static_cast<T*>(base());
+    }
+
+    void Reset();
+
+   private:
+    void* base_address_ = nullptr;
+    size_t size_ = 0;
+  };
+
+  // Constructs an invalid Memory object which cannot be mapped.
+  Memory();
+
+  // Constructs a new Memory object over `handle`, an OSHandle which should have
+  // been previously taken from some other valid Memory object. `size` must
+  // correspond to the size of that original region.
+  Memory(OSHandle handle, size_t size);
+
+  // Constructs a new Memory object over a newly allocated shared memory region
+  // of at least `size` bytes.
+  explicit Memory(size_t size);
+
+  Memory(Memory&&);
+  Memory& operator=(Memory&&);
+  Memory(const Memory&) = delete;
+  Memory& operator=(const Memory&) = delete;
+  ~Memory();
+
+  size_t size() const { return size_; }
+  bool is_valid() const { return handle_.is_valid(); }
+  const OSHandle& handle() const { return handle_; }
+
+  // Invalidates this Memory object and returns an OSHandle which can be used
+  // later to reconstruct an equivalent Memory object, given the same size().
+  OSHandle TakeHandle() { return std::move(handle_); }
+
+  // Resets this object, closing its handle to the underlying region.
+  void reset() { handle_.reset(); }
+
+  // Returns a new Memory object with its own handle to the same underlying
+  // region as `this`. Must only be called on a valid Memory object (i.e.
+  // is_valid() must be true.)
+  Memory Clone();
+
+  // Maps the entire region owned by Memory and returns a Mapping for it. Must
+  // only be called on a valid Memory object (i.e. is_valid() must be true.)
+  Mapping Map();
+
+ private:
+  OSHandle handle_;
+  size_t size_ = 0;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_MEMORY_H_
diff --git a/third_party/ipcz/src/reference_drivers/memory_android.cc b/third_party/ipcz/src/reference_drivers/memory_android.cc
new file mode 100644
index 0000000..355b20f
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_android.cc
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <cstddef>
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/ashmem/ashmem.h"
+
+namespace ipcz::reference_drivers {
+
+void Memory::Mapping::Reset() {
+  if (base_address_) {
+    munmap(base_address_, size_);
+    base_address_ = nullptr;
+    size_ = 0;
+  }
+}
+
+Memory::Memory(size_t size) {
+  const size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
+  const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
+  int fd = ashmem_create_region("ipcz-memory", rounded_size);
+  ABSL_ASSERT(fd >= 0);
+  int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+  ABSL_ASSERT(err == 0);
+  handle_ = OSHandle(fd);
+  size_ = size;
+}
+
+Memory::Mapping Memory::Map() {
+  ABSL_ASSERT(is_valid());
+  void* addr =
+      mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, handle_.fd(), 0);
+  ABSL_ASSERT(addr && addr != MAP_FAILED);
+  return Mapping(addr, size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc b/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc
new file mode 100644
index 0000000..e2c8e2c
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_fuchsia.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <lib/zx/vmar.h>
+#include <zircon/syscalls.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+void Memory::Mapping::Reset() {
+  if (base_address_) {
+    uintptr_t addr = reinterpret_cast<uintptr_t>(base_address_);
+    zx_status_t status = zx::vmar::root_self()->unmap(addr, size_);
+    ABSL_ASSERT(status == ZX_OK);
+  }
+}
+
+Memory::Memory(size_t size) {
+  const uint32_t page_size = zx_system_get_page_size();
+  const size_t rounded_size = (size + page_size - 1) & (page_size - 1);
+  zx::vmo vmo;
+  zx_status_t status = zx::vmo::create(rounded_size, 0, &vmo);
+  ABSL_ASSERT(status == ZX_OK);
+  const int kNoExec = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE;
+  status = vmo.replace(kNoExec, &vmo);
+  ABSL_ASSERT(status == ZX_OK);
+  handle_ = OSHandle(std::move(vmo));
+  size_ = size;
+}
+
+Memory::Mapping Memory::Map() {
+  ABSL_ASSERT(is_valid());
+  uintptr_t addr;
+  zx_vm_option_t options =
+      ZX_VM_REQUIRE_NON_RESIZABLE | ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
+  zx_status_t status = zx::vmar::root_self()->map(
+      options, /*vmar_offset=*/0, *zx::unowned_vmo(handle_.handle().get()), 0,
+      size_, &addr);
+  if (status != ZX_OK) {
+    return {};
+  }
+  return Mapping(reinterpret_cast<void*>(addr), size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_mac.cc b/third_party/ipcz/src/reference_drivers/memory_mac.cc
new file mode 100644
index 0000000..4f1c561
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_mac.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <mach/mach_vm.h>
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+void Memory::Mapping::Reset() {
+  if (base_address_) {
+    kern_return_t kr = mach_vm_deallocate(
+        mach_task_self(), reinterpret_cast<mach_vm_address_t>(base_address_),
+        size_);
+    ABSL_ASSERT(kr == KERN_SUCCESS);
+  }
+}
+
+Memory::Memory(size_t size) {
+  mach_vm_size_t vm_size = size;
+  mach_port_t named_right;
+  kern_return_t kr = mach_make_memory_entry_64(
+      mach_task_self(), &vm_size, 0,
+      MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, &named_right,
+      MACH_PORT_NULL);
+  ABSL_ASSERT(kr == KERN_SUCCESS);
+  ABSL_ASSERT(vm_size >= size);
+  handle_ = OSHandle(OSHandle::MachSendRight(named_right));
+  size_ = size;
+}
+
+Memory::Mapping Memory::Map() {
+  ABSL_ASSERT(is_valid());
+  mach_vm_address_t address = 0;
+  kern_return_t kr = mach_vm_map(mach_task_self(), &address, size_, 0,
+                                 VM_FLAGS_ANYWHERE, handle_.mach_send_right(),
+                                 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
+                                 VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
+  ABSL_ASSERT(kr == KERN_SUCCESS);
+  return Mapping(reinterpret_cast<void*>(address), size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_posix.cc b/third_party/ipcz/src/reference_drivers/memory_posix.cc
new file mode 100644
index 0000000..37e7775
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_posix.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+void Memory::Mapping::Reset() {
+  if (base_address_) {
+    munmap(base_address_, size_);
+    base_address_ = nullptr;
+    size_ = 0;
+  }
+}
+
+Memory::Memory(size_t size) {
+  int fd = memfd_create("/ipcz/mem", MFD_ALLOW_SEALING);
+  ABSL_ASSERT(fd >= 0);
+
+  int result = ftruncate(fd, size);
+  ABSL_ASSERT(result == 0);
+
+  result = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
+  ABSL_ASSERT(result == 0);
+
+  handle_ = OSHandle(fd);
+  size_ = size;
+}
+
+Memory::Mapping Memory::Map() {
+  ABSL_ASSERT(is_valid());
+  void* addr =
+      mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, handle_.fd(), 0);
+  ABSL_ASSERT(addr && addr != MAP_FAILED);
+  return Mapping(addr, size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_test.cc b/third_party/ipcz/src/reference_drivers/memory_test.cc
new file mode 100644
index 0000000..7017852
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <tuple>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ipcz::reference_drivers {
+namespace {
+
+using MemoryTest = testing::Test;
+
+TEST_F(MemoryTest, CreateAndMap) {
+  Memory memory(64);
+
+  Memory::Mapping mapping0 = memory.Map();
+  Memory::Mapping mapping1 = memory.Map();
+
+  int* data0 = mapping0.As<int>();
+  int* data1 = mapping1.As<int>();
+
+  // Each mapping should have a different base address.
+  EXPECT_NE(data0, data1);
+
+  // But they should be backed by the same physical memory.
+  data1[0] = 0;
+  data0[0] = 42;
+  EXPECT_EQ(42, data1[0]);
+}
+
+TEST_F(MemoryTest, CreateMapClose) {
+  Memory memory(64);
+
+  Memory::Mapping mapping0 = memory.Map();
+  Memory::Mapping mapping1 = memory.Map();
+
+  // Even with the memfd closed, the mappings above should persist.
+  memory.reset();
+
+  int* data0 = mapping0.As<int>();
+  int* data1 = mapping1.As<int>();
+  EXPECT_NE(data0, data1);
+  data1[0] = 0;
+  data0[0] = 42;
+  EXPECT_EQ(42, data1[0]);
+}
+
+TEST_F(MemoryTest, CreateCloneMapClose) {
+  Memory memory(64);
+  Memory clone = memory.Clone();
+
+  Memory::Mapping mapping0 = memory.Map();
+  Memory::Mapping mapping1 = clone.Map();
+
+  memory.reset();
+  clone.reset();
+
+  int* data0 = mapping0.As<int>();
+  int* data1 = mapping1.As<int>();
+  EXPECT_NE(data0, data1);
+  data1[0] = 0;
+  data0[0] = 42;
+  EXPECT_EQ(42, data1[0]);
+}
+
+}  // namespace
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/memory_win.cc b/third_party/ipcz/src/reference_drivers/memory_win.cc
new file mode 100644
index 0000000..77ef5b6
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/memory_win.cc
@@ -0,0 +1,48 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/memory.h"
+
+#include <windows.h>
+
+#include "reference_drivers/os_handle.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "util/safe_math.h"
+
+namespace ipcz::reference_drivers {
+
+void Memory::Mapping::Reset() {
+  if (base_address_) {
+    ::UnmapViewOfFile(base_address_);
+  }
+}
+
+Memory::Memory(size_t size) {
+  HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
+                                 0, checked_cast<DWORD>(size), nullptr);
+  const HANDLE process = ::GetCurrentProcess();
+  HANDLE h2;
+
+  // NOTE: DuplicateHandle is called here to remove some permissions from the
+  // handle (at least WRITE_DAC, among others). This allows the handle to be
+  // duplicated to other processes under strict sandboxing conditions, which may
+  // be useful in some test scenarios.
+  BOOL ok = ::DuplicateHandle(process, h, process, &h2,
+                              FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0);
+  ::CloseHandle(h);
+  ABSL_ASSERT(ok);
+
+  handle_ = OSHandle(h2);
+  size_ = size;
+}
+
+Memory::Mapping Memory::Map() {
+  ABSL_ASSERT(is_valid());
+  void* addr = ::MapViewOfFile(handle_.handle(), FILE_MAP_READ | FILE_MAP_WRITE,
+                               0, 0, size_);
+  ABSL_ASSERT(addr);
+  return Mapping(addr, size_);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle.h b/third_party/ipcz/src/reference_drivers/os_handle.h
new file mode 100644
index 0000000..1ed3802
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
+
+#include "build/build_config.h"
+
+#if BUILDFLAG(IS_WIN)
+#include "reference_drivers/os_handle_win.h"
+#elif BUILDFLAG(IS_MAC)
+#include "reference_drivers/os_handle_mac.h"
+#elif BUILDFLAG(IS_FUCHSIA)
+#include "reference_drivers/os_handle_fuchsia.h"
+#elif BUILDFLAG(IS_POSIX)
+#include "reference_drivers/os_handle_posix.h"
+#else
+#error "Unsupported platform"
+#endif
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc
new file mode 100644
index 0000000..c329b7d2
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/os_handle.h"
+
+#include <lib/zx/handle.h>
+#include <zircon/status.h>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+OSHandle::OSHandle() = default;
+
+OSHandle::OSHandle(zx::handle handle) : handle_(std::move(handle)) {}
+
+OSHandle::OSHandle(OSHandle&& other) = default;
+
+OSHandle& OSHandle::operator=(OSHandle&& other) = default;
+
+OSHandle::~OSHandle() = default;
+
+void OSHandle::reset() {
+  handle_.reset();
+}
+
+OSHandle OSHandle::Clone() const {
+  ABSL_ASSERT(is_valid());
+
+  zx::handle dupe;
+  zx_status_t status = handle_.duplicate(ZX_RIGHT_SAME_RIGHTS, &dupe);
+  ABSL_ASSERT(status == ZX_OK);
+  return OSHandle(std::move(dupe));
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h
new file mode 100644
index 0000000..4f03657
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_fuchsia.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
+
+#include <lib/zx/handle.h>
+
+namespace ipcz::reference_drivers {
+
+// The Fuchsia OSHandle implementation can wrap any zx::handle.
+class OSHandle {
+ public:
+  OSHandle();
+  explicit OSHandle(zx::handle handle);
+
+  OSHandle(const OSHandle&) = delete;
+  OSHandle& operator=(const OSHandle&) = delete;
+
+  OSHandle(OSHandle&& other);
+  OSHandle& operator=(OSHandle&& other);
+
+  ~OSHandle();
+
+  void reset();
+
+  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
+  // handle must be valid.
+  OSHandle Clone() const;
+
+  bool is_valid() const { return handle_.is_valid(); }
+  const zx::handle& handle() const { return handle_; }
+
+ private:
+  zx::handle handle_;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_FUCHSIA_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_mac.cc b/third_party/ipcz/src/reference_drivers/os_handle_mac.cc
new file mode 100644
index 0000000..a62061e
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_mac.cc
@@ -0,0 +1,76 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/os_handle.h"
+
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+OSHandle::OSHandle() = default;
+
+OSHandle::OSHandle(Value value) : value_(value) {}
+
+OSHandle::OSHandle(OSHandle&& other)
+    : value_(std::exchange(other.value_, {})) {}
+
+OSHandle& OSHandle::operator=(OSHandle&& other) {
+  reset();
+  value_ = std::exchange(other.value_, {});
+  return *this;
+}
+
+OSHandle::~OSHandle() {
+  reset();
+}
+
+void OSHandle::reset() {
+  if (is_valid_fd()) {
+    int rv = close(fd());
+    ABSL_ASSERT(rv == 0 || errno == EINTR);
+  } else if (is_valid_mach_send_right()) {
+    kern_return_t kr =
+        mach_port_deallocate(mach_task_self(), mach_send_right());
+    ABSL_ASSERT(kr == KERN_SUCCESS);
+  } else if (is_valid_mach_receive_right()) {
+    kern_return_t kr = mach_port_mod_refs(
+        mach_task_self(), mach_receive_right(), MACH_PORT_RIGHT_RECEIVE, -1);
+    ABSL_ASSERT(kr == KERN_SUCCESS);
+  }
+
+  value_ = {};
+}
+
+OSHandle OSHandle::Clone() const {
+  ABSL_ASSERT(is_valid());
+
+  // Cloning of receive rights is not supported.
+  ABSL_ASSERT(!absl::holds_alternative<MachReceiveRight>(value_));
+
+  if (is_valid_fd()) {
+    int duped_fd = dup(fd());
+    ABSL_ASSERT(duped_fd >= 0);
+    return OSHandle(FileDescriptor(duped_fd));
+  }
+
+  if (is_valid_mach_send_right()) {
+    kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_send_right(),
+                                          MACH_PORT_RIGHT_SEND, 1);
+    if (kr != KERN_SUCCESS) {
+      return OSHandle();
+    }
+    return OSHandle(MachSendRight(mach_send_right()));
+  }
+
+  return {};
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_mac.h b/third_party/ipcz/src/reference_drivers/os_handle_mac.h
new file mode 100644
index 0000000..1c9d7e2
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_mac.h
@@ -0,0 +1,84 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
+
+#include <mach/mach.h>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "util/strong_alias.h"
+
+namespace ipcz::reference_drivers {
+
+// The macOS implementation of OSHandle supports wrapping a single Mach send
+// right, or a POSIX file descriptor.
+class OSHandle {
+ public:
+  using FileDescriptor = StrongAlias<class FileDescriptorTag, int>;
+  using MachSendRight = StrongAlias<class MachSendRightTag, mach_port_t>;
+  using MachReceiveRight = StrongAlias<class MachReceiveRightTag, mach_port_t>;
+  using Value = absl::
+      variant<absl::monostate, FileDescriptor, MachSendRight, MachReceiveRight>;
+
+  OSHandle();
+  explicit OSHandle(Value value);
+
+  OSHandle(const OSHandle&) = delete;
+  OSHandle& operator=(const OSHandle&) = delete;
+
+  OSHandle(OSHandle&& other);
+  OSHandle& operator=(OSHandle&& other);
+
+  ~OSHandle();
+
+  void reset();
+
+  // Duplicates the underlying handle, returning a new OSHandle to wrap it.
+  // The handle must be a valid file descriptor or Mach send right. Cloning of
+  // of Mach receive rights is not supported.
+  OSHandle Clone() const;
+
+  bool is_valid() const {
+    return is_valid_fd() || is_valid_mach_send_right() ||
+           is_valid_mach_receive_right();
+  }
+
+  bool is_valid_fd() const {
+    return absl::holds_alternative<FileDescriptor>(value_) && fd() != -1;
+  }
+
+  bool is_valid_mach_send_right() const {
+    return absl::holds_alternative<MachSendRight>(value_) &&
+           mach_send_right() != MACH_PORT_NULL;
+  }
+
+  bool is_valid_mach_receive_right() const {
+    return absl::holds_alternative<MachReceiveRight>(value_) &&
+           mach_receive_right() != MACH_PORT_NULL;
+  }
+
+  int fd() const {
+    ABSL_ASSERT(is_valid_fd());
+    return absl::get<FileDescriptor>(value_).value();
+  }
+
+  mach_port_t mach_send_right() const {
+    ABSL_ASSERT(is_valid_mach_send_right());
+    return absl::get<MachSendRight>(value_).value();
+  }
+
+  mach_port_t mach_receive_right() const {
+    ABSL_ASSERT(is_valid_mach_receive_right());
+    return absl::get<MachReceiveRight>(value_).value();
+  }
+
+ private:
+  Value value_;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_MAC_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_posix.cc b/third_party/ipcz/src/reference_drivers/os_handle_posix.cc
new file mode 100644
index 0000000..c96dbdf
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_posix.cc
@@ -0,0 +1,46 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/os_handle.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+OSHandle::OSHandle() = default;
+
+OSHandle::OSHandle(int fd) : fd_(fd) {}
+
+OSHandle::OSHandle(OSHandle&& other) : fd_(std::exchange(other.fd_, -1)) {}
+
+OSHandle& OSHandle::operator=(OSHandle&& other) {
+  reset();
+  fd_ = std::exchange(other.fd_, -1);
+  return *this;
+}
+
+OSHandle::~OSHandle() {
+  reset();
+}
+
+void OSHandle::reset() {
+  int fd = std::exchange(fd_, -1);
+  if (fd >= 0) {
+    int rv = close(fd);
+    ABSL_ASSERT(rv == 0 || errno == EINTR);
+  }
+}
+
+OSHandle OSHandle::Clone() const {
+  ABSL_ASSERT(is_valid());
+  int dupe = dup(fd_);
+  return OSHandle(dupe);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_posix.h b/third_party/ipcz/src/reference_drivers/os_handle_posix.h
new file mode 100644
index 0000000..0ab5efc2
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_posix.h
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
+
+namespace ipcz::reference_drivers {
+
+// The POSIX OSHandle implementation wraps a file descriptor.
+class OSHandle {
+ public:
+  OSHandle();
+  explicit OSHandle(int fd);
+
+  OSHandle(const OSHandle&) = delete;
+  OSHandle& operator=(const OSHandle&) = delete;
+
+  OSHandle(OSHandle&& other);
+  OSHandle& operator=(OSHandle&& other);
+
+  ~OSHandle();
+
+  void reset();
+
+  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
+  // handle must be valid.
+  OSHandle Clone() const;
+
+  bool is_valid() const { return fd_ != -1; }
+
+  int fd() const { return fd_; }
+
+ private:
+  int fd_ = -1;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_POSIX_H_
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_win.cc b/third_party/ipcz/src/reference_drivers/os_handle_win.cc
new file mode 100644
index 0000000..570f45ea
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_win.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "reference_drivers/os_handle.h"
+
+#include <windows.h>
+
+#include <utility>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::reference_drivers {
+
+OSHandle::OSHandle() = default;
+
+OSHandle::OSHandle(HANDLE handle) : handle_(handle) {}
+
+OSHandle::OSHandle(OSHandle&& other)
+    : handle_(std::exchange(other.handle_, INVALID_HANDLE_VALUE)) {}
+
+OSHandle& OSHandle::operator=(OSHandle&& other) {
+  reset();
+  handle_ = std::exchange(other.handle_, INVALID_HANDLE_VALUE);
+  return *this;
+}
+
+OSHandle::~OSHandle() {
+  reset();
+}
+
+void OSHandle::reset() {
+  HANDLE handle = std::exchange(handle_, INVALID_HANDLE_VALUE);
+  if (handle != INVALID_HANDLE_VALUE) {
+    ::CloseHandle(handle);
+  }
+}
+
+OSHandle OSHandle::Clone() const {
+  ABSL_ASSERT(is_valid());
+
+  HANDLE dupe;
+  BOOL result =
+      ::DuplicateHandle(::GetCurrentProcess(), handle_, ::GetCurrentProcess(),
+                        &dupe, 0, FALSE, DUPLICATE_SAME_ACCESS);
+  if (!result) {
+    return OSHandle();
+  }
+
+  ABSL_ASSERT(dupe != INVALID_HANDLE_VALUE);
+  return OSHandle(dupe);
+}
+
+}  // namespace ipcz::reference_drivers
diff --git a/third_party/ipcz/src/reference_drivers/os_handle_win.h b/third_party/ipcz/src/reference_drivers/os_handle_win.h
new file mode 100644
index 0000000..2506f95
--- /dev/null
+++ b/third_party/ipcz/src/reference_drivers/os_handle_win.h
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
+#define IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
+
+#include <windows.h>
+
+namespace ipcz::reference_drivers {
+
+// The Windows OSHandle implementation can wrap any HANDLE value.
+class OSHandle {
+ public:
+  OSHandle();
+  explicit OSHandle(HANDLE handle);
+
+  OSHandle(const OSHandle&) = delete;
+  OSHandle& operator=(const OSHandle&) = delete;
+
+  OSHandle(OSHandle&& other);
+  OSHandle& operator=(OSHandle&& other);
+
+  ~OSHandle();
+
+  void reset();
+
+  // Duplicates the underlying handle, returning a new OSHandle to wrap it. The
+  // handle must be valid.
+  OSHandle Clone() const;
+
+  bool is_valid() const { return handle_ != INVALID_HANDLE_VALUE; }
+
+  HANDLE handle() const { return handle_; }
+
+ private:
+  HANDLE handle_ = INVALID_HANDLE_VALUE;
+};
+
+}  // namespace ipcz::reference_drivers
+
+#endif  // IPCZ_SRC_REFERENCE_DRIVERS_OS_HANDLE_WIN_H_
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index ece1e5a2..78dfc930 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -381,11 +381,11 @@
       'ios-simulator-cronet': 'ios_cronet_xctest',
       'ios-simulator-multi-window': 'ios_simulator_debug_static_bot_xctest',
       'ios-webkit-tot': 'ios_simulator_debug_static_bot_xctest_no_lld',
-      'ios14-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
-      'ios14-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios15-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios15-sdk-device': 'ios_device_release_static_bot_xctest',
       'ios15-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
+      'ios16-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
+      'ios16-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
       'lacros-amd64-generic-rel (goma cache silo)': 'chromeos_amd64-generic_lacros_rel',
       'lacros-amd64-generic-rel (reclient)': 'chromeos_amd64-generic-vm_lacros_rel_reclient',
       'lacros-amd64-generic-rel-fyi': 'chromeos_amd64-generic_lacros_rel_reclient',
@@ -1160,10 +1160,10 @@
       'ios-simulator-multi-window': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-noncq': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-rts': 'ios_simulator_code_coverage_partial_instrumentation_xctest',
-      'ios14-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
-      'ios14-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios15-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
       'ios15-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
+      'ios16-beta-simulator': 'ios_simulator_debug_static_bot_xctest',
+      'ios16-sdk-simulator': 'ios_simulator_debug_static_bot_xctest',
       'mac-arm64-on-arm64-rel': 'mac_arm64_release_trybot',
       'mac-builder-next-rel': 'mac_arm64_gpu_tests_release_bot_minimal_symbols_no_nacl',
       'mac-clang-tidy-rel': 'release_trybot',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 3f28e21..d86dc71b 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -804,32 +804,6 @@
       "use_lld": false
     }
   },
-  "ios14-beta-simulator": {
-    "gn_args": {
-      "enable_run_ios_unittests_with_xctest": true,
-      "ios_set_attributes_for_xcode_project_generation": false,
-      "is_component_build": false,
-      "is_debug": true,
-      "symbol_level": 1,
-      "target_cpu": "x64",
-      "target_environment": "simulator",
-      "target_os": "ios",
-      "use_goma": true
-    }
-  },
-  "ios14-sdk-simulator": {
-    "gn_args": {
-      "enable_run_ios_unittests_with_xctest": true,
-      "ios_set_attributes_for_xcode_project_generation": false,
-      "is_component_build": false,
-      "is_debug": true,
-      "symbol_level": 1,
-      "target_cpu": "x64",
-      "target_environment": "simulator",
-      "target_os": "ios",
-      "use_goma": true
-    }
-  },
   "ios15-beta-simulator": {
     "gn_args": {
       "enable_run_ios_unittests_with_xctest": true,
@@ -870,6 +844,32 @@
       "use_goma": true
     }
   },
+  "ios16-beta-simulator": {
+    "gn_args": {
+      "enable_run_ios_unittests_with_xctest": true,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": true,
+      "symbol_level": 1,
+      "target_cpu": "x64",
+      "target_environment": "simulator",
+      "target_os": "ios",
+      "use_goma": true
+    }
+  },
+  "ios16-sdk-simulator": {
+    "gn_args": {
+      "enable_run_ios_unittests_with_xctest": true,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": true,
+      "symbol_level": 1,
+      "target_cpu": "x64",
+      "target_environment": "simulator",
+      "target_os": "ios",
+      "use_goma": true
+    }
+  },
   "lacros-amd64-generic-rel (goma cache silo)": {
     "args_file": "//build/args/chromeos/amd64-generic-crostoolchain.gni",
     "gn_args": {
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
index cbd24f5..1b53e5f 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
@@ -412,32 +412,6 @@
       "use_goma": true
     }
   },
-  "ios14-beta-simulator": {
-    "gn_args": {
-      "enable_run_ios_unittests_with_xctest": true,
-      "ios_set_attributes_for_xcode_project_generation": false,
-      "is_component_build": false,
-      "is_debug": true,
-      "symbol_level": 1,
-      "target_cpu": "x64",
-      "target_environment": "simulator",
-      "target_os": "ios",
-      "use_goma": true
-    }
-  },
-  "ios14-sdk-simulator": {
-    "gn_args": {
-      "enable_run_ios_unittests_with_xctest": true,
-      "ios_set_attributes_for_xcode_project_generation": false,
-      "is_component_build": false,
-      "is_debug": true,
-      "symbol_level": 1,
-      "target_cpu": "x64",
-      "target_environment": "simulator",
-      "target_os": "ios",
-      "use_goma": true
-    }
-  },
   "ios15-beta-simulator": {
     "gn_args": {
       "enable_run_ios_unittests_with_xctest": true,
@@ -464,6 +438,32 @@
       "use_goma": true
     }
   },
+  "ios16-beta-simulator": {
+    "gn_args": {
+      "enable_run_ios_unittests_with_xctest": true,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": true,
+      "symbol_level": 1,
+      "target_cpu": "x64",
+      "target_environment": "simulator",
+      "target_os": "ios",
+      "use_goma": true
+    }
+  },
+  "ios16-sdk-simulator": {
+    "gn_args": {
+      "enable_run_ios_unittests_with_xctest": true,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": true,
+      "symbol_level": 1,
+      "target_cpu": "x64",
+      "target_environment": "simulator",
+      "target_os": "ios",
+      "use_goma": true
+    }
+  },
   "mac-arm64-on-arm64-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6d0dec1..3f3091b4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -38598,7 +38598,7 @@
   <int value="2774"
       label="OBSOLETE_CSSValueAppearanceButtonForNonButtonRendered"/>
   <int value="2775" label="OBSOLETE_CSSValueAppearanceButtonForOthersRendered"/>
-  <int value="2776" label="CustomCursorIntersectsViewport"/>
+  <int value="2776" label="OBSOLETE_CustomCursorIntersectsViewport"/>
   <int value="2777" label="OBSOLETE_ClientHintsLang"/>
   <int value="2778" label="LinkRelPreloadImageSrcset"/>
   <int value="2779" label="V8HTMLMediaElement_Remote_AttributeGetter"/>
@@ -41681,6 +41681,14 @@
   <int value="2" label="Unable to open"/>
 </enum>
 
+<enum name="FileManagerExtractStatus">
+  <int value="0" label="Success"/>
+  <int value="1" label="Cancelled"/>
+  <int value="2" label="Low Disk Space"/>
+  <int value="3" label="Bad Password"/>
+  <int value="4" label="Unknown Error"/>
+</enum>
+
 <enum name="FileManagerFormatFileSystemType">
   <int value="0" label="Unknown"/>
   <int value="1" label="FAT32"/>
@@ -57803,6 +57811,8 @@
   <int value="75747474" label="disable-webview-signin-flow"/>
   <int value="77886794" label="AllowRepeatedUpdates:enabled"/>
   <int value="77946316" label="PrefixWebAppWindowsWithAppName:disabled"/>
+  <int value="78900745"
+      label="SharedHighlightingRefinedMaxContextWords:disabled"/>
   <int value="78998551" label="disable-hosted-app-shim-creation"/>
   <int value="79094339" label="VrLaunchIntents:enabled"/>
   <int value="79503461" label="disable-account-consistency"/>
@@ -59518,6 +59528,8 @@
   <int value="1226624874" label="Mus:disabled"/>
   <int value="1226676061" label="PageInfoV2Desktop:enabled"/>
   <int value="1226760549" label="LauncherGameSearch:enabled"/>
+  <int value="1226782541"
+      label="SharedHighlightingRefinedMaxContextWords:enabled"/>
   <int value="1227633129" label="NtpChromeCartModule:disabled"/>
   <int value="1228054141"
       label="OverrideUnsupportedPageLanguageForHrefTranslate:enabled"/>
@@ -84321,6 +84333,8 @@
   <int value="12" label="At least one segment does not have default signals"/>
   <int value="13" label="At least one segment default model execution failed"/>
   <int value="14" label="At least one segment default model missing metadata"/>
+  <int value="15"
+      label="At least one segment on demand tflite model execution failed"/>
 </enum>
 
 <enum name="SelectedNewTabCreationOption">
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index a3752c2a..7732c62 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -490,6 +490,17 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Clipboard.HasTransientUserActivation" enum="Boolean"
+    expires_after="2022-08-01">
+  <owner>asully@chromium.org</owner>
+  <owner>snianu@microsoft.com</owner>
+  <owner>chrome-owp-storage@google.com</owner>
+  <summary>
+    Records if a transient user activation is present or not when the web
+    authors execute the async clipboard read/write call.
+  </summary>
+</histogram>
+
 <histogram name="Blink.ColorGamut.Destination" enum="Gamut" expires_after="M85">
   <owner>brianosman@chromium.org</owner>
   <owner>mcasas@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index a9322b4..3185a97e 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -42,6 +42,10 @@
     <owner>cros-connectivity@google.com</owner>
     <owner>hsuregan@chromium.org</owner>
   </variant>
+  <variant name="FakeKeyboardHeuristic" summary="Fake Keyboard Heuristic">
+    <owner>wmahon@chromium.org</owner>
+    <owner>chromeos-tango@google.com</owner>
+  </variant>
   <variant name="FastPair" summary="Fast Pair">
     <owner>shanefitz@google.com</owner>
     <owner>julietlevesque@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 2d7a23d..0cce5fa 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -351,19 +351,6 @@
 </histogram>
 
 <histogram
-    name="Compositing.Display.OverlayProcessorOzone.IsCandidateSharedImage"
-    enum="Boolean" expires_after="2021-09-19">
-  <owner>samans@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    Indicates whether the OverlayCandidate passed to
-    OverlayCandidateValidatorOzone is backed by a SharedImage (as opposed to a
-    legacy mailbox). Recorded for every candidate in every overlay configuration
-    passed to OverlayProcessorOzone.
-  </summary>
-</histogram>
-
-<histogram
     name="Compositing.Display.OverlayProcessorOzone.MaxOverlaysSupported"
     units="units" expires_after="2022-11-30">
   <owner>khaslett@chromium.org</owner>
@@ -376,16 +363,6 @@
   </summary>
 </histogram>
 
-<histogram name="Compositing.Display.OverlayProcessorOzone.SharedImageExists"
-    enum="Boolean" expires_after="2021-09-19">
-  <owner>samans@chromium.org</owner>
-  <owner>rjkroege@chromium.org</owner>
-  <summary>
-    Indicates whether the SharedImage corresponding to the OverlayCandidate was
-    found. Reported for every OverlayCandidate that is backed by a SharedImage.
-  </summary>
-</histogram>
-
 <histogram
     name="Compositing.Display.OverlayProcessorUsingStrategy.CandidateCombinationPreviouslySucceeded"
     enum="Boolean" expires_after="2022-11-30">
diff --git a/tools/metrics/histograms/metadata/direct/histograms.xml b/tools/metrics/histograms/metadata/direct/histograms.xml
index dc06431..625c947 100644
--- a/tools/metrics/histograms/metadata/direct/histograms.xml
+++ b/tools/metrics/histograms/metadata/direct/histograms.xml
@@ -22,91 +22,6 @@
 
 <histograms>
 
-<histogram
-    name="DirectWrite.Fonts.Content.FallbackFamilyAndStyleForCodepointTime"
-    units="microseconds" expires_after="2022-06-15">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::FallbackFamilyAndStyleForCodepoint.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.FindFamilyTime" units="microseconds"
-    expires_after="2022-10-16">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::FindFamily.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.GetFamilyCountTime"
-    units="microseconds" expires_after="2022-10-16">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::GetFamilyCount.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.GetFamilyNamesTime"
-    units="microseconds" expires_after="2022-06-15">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::GetFamilyNames.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.GetFontFilesTime"
-    units="microseconds" expires_after="2022-10-16">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::GetFontFiles.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.InitializeTime" units="microseconds"
-    expires_after="2022-06-15">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::Initialize.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
-<histogram name="DirectWrite.Fonts.Content.MapCharactersTime"
-    units="microseconds" expires_after="2022-10-16">
-  <owner>sky@chromium.org</owner>
-  <owner>jam@chromium.org</owner>
-  <summary>
-    The time in microseconds it takes to execute
-    DWriteFontProxyImpl::MapCharacters.
-
-    This metric is reported for all users.
-  </summary>
-</histogram>
-
 <histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" units="units"
     expires_after="2022-10-15">
   <owner>drott@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 1c3141c..f58fff3 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -275,6 +275,13 @@
   <summary>Tracks success rate of pinning files in Drive.</summary>
 </histogram>
 
+<histogram name="FileBrowser.ExtractTask.Status"
+    enum="FileManagerExtractStatus" expires_after="M113">
+  <owner>adanilo@chromium.org</owner>
+  <owner>src/ui/file_manager/OWNERS</owner>
+  <summary>Tracks success/error rate of extracting archive contents.</summary>
+</histogram>
+
 <histogram name="FileBrowser.FileSystemProviderMounted"
     enum="FileSystemProviderMountType" expires_after="2023-04-10">
   <owner>simmonsjosh@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 54c0658..858967e 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -1028,6 +1028,25 @@
   </token>
 </histogram>
 
+<histogram name="History.Clusters.WebUISessionDuration" units="ms"
+    expires_after="2022-11-30">
+  <owner>tommycli@chromium.org</owner>
+  <owner>chrome-journeys@google.com</owner>
+  <summary>
+    Records the amount of time the Journeys WebUI is open.
+
+    This timer is started when the Journeys WebUI is loaded. The Journeys WebUI
+    is loaded when either the user directly goes to the Journeys WebUI, or when
+    the user switches to the Journeys tab from a different tab.
+
+    The timer is stopped and the elapsed time recorded when either the whole
+    WebUI is closed, or if the user disables the Journeys preference, or if the
+    user switches tabs off the Journeys tab onto a different one.
+
+    Elapsed times longer than one hour are recorded as one hour.
+  </summary>
+</histogram>
+
 <histogram name="History.DatabaseAdvancedMetricsTime" units="ms"
     expires_after="M77">
   <owner>shess@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 116c95f..8dfefb5 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -67,7 +67,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.Actions" enum="LanguageSettingsActionType"
-    expires_after="2022-06-30">
+    expires_after="2022-09-11">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -132,7 +132,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.Language"
-    enum="LocaleCodeISO639" expires_after="2022-06-30">
+    enum="LocaleCodeISO639" expires_after="2022-09-11">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -287,7 +287,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.UI.Android.Correctness"
-    enum="LanguageUsage.UI.Android.Correctness" expires_after="2022-06-30">
+    enum="LanguageUsage.UI.Android.Correctness" expires_after="2022-09-11">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -410,7 +410,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.Status{AccountType}"
-    enum="ULPInitiationStatus" expires_after="2022-06-30">
+    enum="ULPInitiationStatus" expires_after="2022-09-11">
   <owner>jds@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index a8a6970..f3cf390 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -714,7 +714,19 @@
   <summary>
     The percentage of CPU time that the Missive daemon has been using. The CPU
     usage is not expected to be high and any usage beyond 100% is considered to
-    be in the overflow bucket. This is reported once every 10 minutes.
+    be in the overflow bucket. This is reported once every 10 minutes and only
+    if the Missive daemon is running.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Missive.MemoryUsage" units="0.1MiB"
+    expires_after="2023-05-03">
+  <owner>xuhong@chromium.org</owner>
+  <owner>lbaraz@chromium.org</owner>
+  <owner>cros-reporting-team@google.com</owner>
+  <summary>
+    The amount of memory that the Missive daemon has been using. This is
+    reported once every 10 minutes and only if the Missive daemon is running.
   </summary>
 </histogram>
 
@@ -725,7 +737,8 @@
   <owner>cros-reporting-team@google.com</owner>
   <summary>
     The amount of disk storage that the Missive daemon has been using for
-    storing encrypted records. This is reported once every 10 minutes.
+    storing encrypted records. This is reported once every 10 minutes and only
+    if the Missive daemon is running.
   </summary>
 </histogram>
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 8a33c29..90d9a37 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "ce86af6a7e4d2258fa27e5356ff6663139e9dea5",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6ebcd58ed94f88099a1d8ab5fb28fa28bc0d3e9a/trace_processor_shell.exe"
+            "hash": "dfa666ba78c81ad93ac36b4893c39fde6419e661",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/370def59dcc5b6423f805754160cab7c5b772c2d/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "f9e92827759f3431ba8ce181abb1fb48187cfe84",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/6ebcd58ed94f88099a1d8ab5fb28fa28bc0d3e9a/trace_processor_shell"
+            "hash": "64fd82d275dcd99827bdd14de3d41f70c6363a3c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/a4217a18debea8b9f02ab15f65097533568bf810/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
             "full_remote_path": "perfetto-luci-artifacts/v25.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "1cd581f6e67132e4a29f8316fe516c3bd42ab3ff",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/6ebcd58ed94f88099a1d8ab5fb28fa28bc0d3e9a/trace_processor_shell"
+            "hash": "cba2db3019906c1d886a8436d402bd359af1eec7",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/370def59dcc5b6423f805754160cab7c5b772c2d/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/aura/screen_ozone.cc b/ui/aura/screen_ozone.cc
index d133110..a78a6a4 100644
--- a/ui/aura/screen_ozone.cc
+++ b/ui/aura/screen_ozone.cc
@@ -13,10 +13,13 @@
 
 namespace aura {
 
-ScreenOzone::ScreenOzone() = default;
+ScreenOzone::ScreenOzone() {
+  DCHECK(!display::Screen::HasScreen());
+  display::Screen::SetScreenInstance(this);
+}
 
 ScreenOzone::~ScreenOzone() {
-  display::Screen::SetScreenInstance(old_screen_);
+  display::Screen::SetScreenInstance(nullptr);
 }
 
 void ScreenOzone::Initialize() {
@@ -34,6 +37,11 @@
   }
 }
 
+// static
+bool ScreenOzone::IsOzoneInitialized() {
+  return ui::OzonePlatform::IsInitialized();
+}
+
 gfx::Point ScreenOzone::GetCursorScreenPoint() {
   return platform_screen_->GetCursorScreenPoint();
 }
@@ -149,4 +157,17 @@
 
 void ScreenOzone::OnBeforePlatformScreenInit() {}
 
+ScopedScreenOzone::ScopedScreenOzone(const base::Location& location)
+    : ScopedNativeScreen(/*call_maybe_init=*/false, location) {
+  MaybeInit();
+}
+
+ScopedScreenOzone::~ScopedScreenOzone() = default;
+
+display::Screen* ScopedScreenOzone::CreateScreen() {
+  auto* screen = new ScreenOzone();
+  screen->Initialize();
+  return screen;
+}
+
 }  // namespace aura
diff --git a/ui/aura/screen_ozone.h b/ui/aura/screen_ozone.h
index 980c083b..2970a0e 100644
--- a/ui/aura/screen_ozone.h
+++ b/ui/aura/screen_ozone.h
@@ -60,6 +60,8 @@
   virtual gfx::NativeWindow GetNativeWindowFromAcceleratedWidget(
       gfx::AcceleratedWidget widget) const;
 
+  static bool IsOzoneInitialized();
+
  protected:
   ui::PlatformScreen* platform_screen() { return platform_screen_.get(); }
 
@@ -73,10 +75,22 @@
 
   virtual void OnBeforePlatformScreenInit();
 
-  display::Screen* const old_screen_ = display::Screen::SetScreenInstance(this);
   std::unique_ptr<ui::PlatformScreen> platform_screen_;
 };
 
+// ScopedScreenOzone creates a ScreenOzone instead of NativeScreen
+// (created by `CreateNativeScreen()`) if the screen hasn't been set.
+class AURA_EXPORT ScopedScreenOzone : public display::ScopedNativeScreen {
+ public:
+  explicit ScopedScreenOzone(const base::Location& location = FROM_HERE);
+  ScopedScreenOzone(const ScopedScreenOzone&) = delete;
+  ScopedScreenOzone operator=(const ScopedScreenOzone&) = delete;
+  ~ScopedScreenOzone() override;
+
+ private:
+  display::Screen* CreateScreen() override;
+};
+
 }  // namespace aura
 
 #endif  // UI_AURA_SCREEN_OZONE_H_
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 4c996a80..2efba1d 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -541,6 +541,13 @@
     deps += [ "//third_party/fontconfig" ]
   }
 
+  if (is_chromeos_lacros) {
+    deps += [
+      "//chromeos/crosapi/cpp:crosapi_constants",
+      "//chromeos/lacros:lacros_paths",
+    ]
+  }
+
   if (use_glib) {
     configs += [ "//build/config/linux:glib" ]
     sources += [
diff --git a/ui/base/DEPS b/ui/base/DEPS
index be9a7fd..d5f0903 100644
--- a/ui/base/DEPS
+++ b/ui/base/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+  "+chromeos/crosapi/cpp/crosapi_constants.h",
+  "+chromeos/lacros/lacros_paths.h",
   "+components/vector_icons",
   "+components/version_info",
   "+media/media_buildflags.h",
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 2888b12..2222f4a 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -98,6 +98,7 @@
 
 ResourceBundle* g_shared_instance_ = nullptr;
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 base::FilePath GetResourcesPakFilePath(const std::string& pak_name) {
   base::FilePath path;
   if (base::PathService::Get(base::DIR_ASSETS, &path))
@@ -110,6 +111,7 @@
   return base::FilePath(pak_name.c_str());
 #endif  // BUILDFLAG(IS_WIN)
 }
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 SkBitmap CreateEmptyBitmap() {
   SkBitmap bitmap;
@@ -949,6 +951,7 @@
 #endif
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 void ResourceBundle::LoadChromeResources() {
   // Always load the 1x data pack first as the 2x data pack contains both 1x and
   // 2x images. The 1x data pack only has 1x images, thus passes in an accurate
@@ -963,6 +966,7 @@
         GetResourcesPakFilePath("chrome_200_percent.pak"), k200Percent);
   }
 }
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 void ResourceBundle::AddDataPackFromPathInternal(
     const base::FilePath& path,
diff --git a/ui/base/resource/resource_bundle_lacros.cc b/ui/base/resource/resource_bundle_lacros.cc
index 3badf24..fdfc963 100644
--- a/ui/base/resource/resource_bundle_lacros.cc
+++ b/ui/base/resource/resource_bundle_lacros.cc
@@ -4,11 +4,42 @@
 
 #include "ui/base/resource/resource_bundle.h"
 
+#include "base/command_line.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/path_service.h"
+#include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/lacros/lacros_paths.h"
 #include "ui/base/resource/data_pack_with_resource_sharing_lacros.h"
+#include "ui/base/ui_base_switches.h"
 
 namespace ui {
 
+namespace {
+
+constexpr char k100PercentPack[] = "chrome_100_percent.pak";
+constexpr char k200PercentPack[] = "chrome_200_percent.pak";
+
+base::FilePath GetResourcesPakFilePath(const std::string& pak_name) {
+  base::FilePath path;
+  if (base::PathService::Get(base::DIR_ASSETS, &path))
+    return path.AppendASCII(pak_name.c_str());
+
+  // Return just the name of the pak file.
+  return base::FilePath(pak_name.c_str());
+}
+
+base::FilePath GetGeneratedFilePath(const std::string& pak_name) {
+  base::FilePath path;
+  if (base::PathService::Get(chromeos::lacros_paths::USER_DATA_DIR, &path))
+    return path.AppendASCII(pak_name.c_str());
+
+  // Return just the name of the pak file.
+  return base::FilePath(pak_name.c_str());
+}
+
+}  // namespace
+
 void ResourceBundle::AddDataPackFromPathWithAshResourcesInternal(
     const base::FilePath& shared_resource_path,
     const base::FilePath& ash_path,
@@ -23,7 +54,8 @@
         std::make_unique<DataPack>(scale_factor);
     if (data_pack_with_lacros_resource->LoadFromPath(lacros_path)) {
       LOG(WARNING)
-          << "Failed to load shared resource data pack from file. "
+          << "Failed to load shared resource data pack " << shared_resource_path
+          << ": "
           << "Use lacros resource data pack instead of shared resource "
           << "data pack.";
       AddResourceHandle(std::move(data_pack_with_lacros_resource));
@@ -52,4 +84,43 @@
                                               lacros_path, scale_factor, true);
 }
 
+void ResourceBundle::LoadChromeResources() {
+  // Always load the 1x data pack first as the 2x data pack contains both 1x and
+  // 2x images. The 1x data pack only has 1x images, thus passes in an accurate
+  // scale factor to gfx::ImageSkia::AddRepresentation.
+
+  base::FilePath ash_resources_dir;
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kEnableResourcesFileSharing) ||
+      !base::PathService::Get(chromeos::lacros_paths::ASH_RESOURCES_DIR,
+                              &ash_resources_dir)) {
+    // If resource sharing feature is not enabled or ash resources dir path is
+    // not available, use DataPack instead.
+    if (IsScaleFactorSupported(k100Percent)) {
+      AddDataPackFromPath(GetResourcesPakFilePath(k100PercentPack),
+                          k100Percent);
+    }
+
+    if (IsScaleFactorSupported(k200Percent)) {
+      AddOptionalDataPackFromPath(GetResourcesPakFilePath(k200PercentPack),
+                                  k200Percent);
+    }
+    return;
+  }
+
+  if (IsScaleFactorSupported(k100Percent)) {
+    AddDataPackFromPathWithAshResources(
+        GetGeneratedFilePath(crosapi::kSharedChrome100PercentPackName),
+        ash_resources_dir.Append(k100PercentPack),
+        GetResourcesPakFilePath(k200PercentPack), k100Percent);
+  }
+
+  if (IsScaleFactorSupported(k200Percent)) {
+    AddOptionalDataPackFromPathWithAshResources(
+        GetGeneratedFilePath(crosapi::kSharedChrome200PercentPackName),
+        ash_resources_dir.Append(k200PercentPack),
+        GetResourcesPakFilePath(k200PercentPack), k200Percent);
+  }
+}
+
 }  // namespace ui
diff --git a/ui/base/test/cocoa_helper.h b/ui/base/test/cocoa_helper.h
index 4ec09b1..c9030fe 100644
--- a/ui/base/test/cocoa_helper.h
+++ b/ui/base/test/cocoa_helper.h
@@ -13,6 +13,7 @@
 #import "base/mac/scoped_nsobject.h"
 #import "base/strings/sys_string_conversions.h"
 #include "testing/platform_test.h"
+#include "ui/display/screen.h"
 
 // CocoaTestHelperWindow behaves differently from a regular NSWindow in the
 // following ways:
@@ -74,6 +75,8 @@
   CocoaTestHelperWindow* test_window();
 
  private:
+  display::ScopedNativeScreen screen_;
+
   // Return a set of currently open windows. Avoiding NSArray so
   // contents aren't retained, the pointer values can only be used for
   // comparison purposes.  Using std::set to make progress-checking
diff --git a/ui/display/display.cc b/ui/display/display.cc
index 3693f89..2871e5b 100644
--- a/ui/display/display.cc
+++ b/ui/display/display.cc
@@ -34,7 +34,7 @@
 // -1.0, we read the forced device scale factor again.
 float g_forced_device_scale_factor = -1.0;
 
-// An alloance error epsilon cauesd by fractional scale factor to produce
+// An allowance error epsilon caused by fractional scale factor to produce
 // expected DP display size.
 constexpr float kDisplaySizeAllowanceEpsilon = 0.01f;
 
diff --git a/ui/display/display.h b/ui/display/display.h
index 5a9b7ec..faaf504 100644
--- a/ui/display/display.h
+++ b/ui/display/display.h
@@ -172,7 +172,7 @@
   gfx::Insets GetWorkAreaInsets() const;
 
   // Sets the device scale factor and display bounds in pixel. This
-  // updates the work are using the same insets between old bounds and
+  // updates the work area using the same insets between old bounds and
   // work area.
   void SetScaleAndBounds(float device_scale_factor,
                          const gfx::Rect& bounds_in_pixel);
diff --git a/ui/display/display_list.h b/ui/display/display_list.h
index 2eb14bc..4c90bea3 100644
--- a/ui/display/display_list.h
+++ b/ui/display/display_list.h
@@ -71,6 +71,9 @@
   bool IsValid() const;
 
   base::ObserverList<DisplayObserver>* observers() { return &observers_; }
+  const base::ObserverList<DisplayObserver>* observers() const {
+    return &observers_;
+  }
 
  private:
   // A non-const version of FindDisplayById.
diff --git a/ui/display/screen.cc b/ui/display/screen.cc
index 2225ae6..b972388 100644
--- a/ui/display/screen.cc
+++ b/ui/display/screen.cc
@@ -31,8 +31,7 @@
 
 // static
 Screen* Screen::GetScreen() {
-#if BUILDFLAG(IS_APPLE)
-  // TODO(scottmg): https://crbug.com/558054
+#if BUILDFLAG(IS_IOS)
   if (!g_screen)
     g_screen = CreateNativeScreen();
 #endif
@@ -40,10 +39,23 @@
 }
 
 // static
-Screen* Screen::SetScreenInstance(Screen* instance) {
+Screen* Screen::SetScreenInstance(Screen* instance,
+                                  const base::Location& location) {
+  // Do not allow screen instance override. The screen object has a lot of
+  // states, such as current display settings as well as observers, and safely
+  // transferring these to new screen implementation is very difficult and not
+  // safe.  If you hit the DCHECK in a test, please look for other examples that
+  // that set a test screen instance in the setup process.
+  DCHECK(!g_screen || !instance || (instance && instance->shutdown_))
+      << "fail=" << location.ToString();
   return std::exchange(g_screen, instance);
 }
 
+// static
+bool Screen::HasScreen() {
+  return !!g_screen;
+}
+
 void Screen::SetCursorScreenPointForTesting(const gfx::Point& point) {
   NOTIMPLEMENTED_LOG_ONCE();
 }
@@ -213,4 +225,49 @@
   return result;
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+
+ScopedNativeScreen::ScopedNativeScreen(const base::Location& location) {
+  MaybeInit(location);
+}
+
+ScopedNativeScreen::ScopedNativeScreen(bool call_maybe_init,
+                                       const base::Location& location) {
+  if (call_maybe_init)
+    MaybeInit(location);
+}
+
+ScopedNativeScreen::~ScopedNativeScreen() {
+  Shutdown();
+}
+
+void ScopedNativeScreen::MaybeInit(const base::Location& location) {
+  maybe_init_called_ = true;
+  if (!Screen::HasScreen()) {
+#if BUILDFLAG(IS_IOS)
+    Screen::GetScreen();
+#else
+    screen_ = base::WrapUnique(CreateScreen());
+    // ScreenOzone and DesktopScreenWin sets the instance by itself.
+    if (Screen::GetScreen() != screen_.get())
+      Screen::SetScreenInstance(screen_.get(), location);
+#endif
+  }
+}
+
+void ScopedNativeScreen::Shutdown() {
+  DCHECK(maybe_init_called_);
+  if (screen_) {
+    DCHECK_EQ(screen_.get(), Screen::GetScreen());
+    Screen::SetScreenInstance(nullptr);
+    screen_.reset();
+  }
+}
+
+Screen* ScopedNativeScreen::CreateScreen() {
+  return CreateNativeScreen();
+}
+
+#endif
+
 }  // namespace display
diff --git a/ui/display/screen.h b/ui/display/screen.h
index 3771a345..a86c5b6 100644
--- a/ui/display/screen.h
+++ b/ui/display/screen.h
@@ -9,8 +9,9 @@
 #include <set>
 #include <vector>
 
+#include "base/location.h"
 #include "base/values.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "ui/display/display.h"
 #include "ui/display/display_export.h"
 #include "ui/display/screen_infos.h"
@@ -47,13 +48,20 @@
 
   virtual ~Screen();
 
-  // Retrieves the single Screen object; this may be null (e.g. in some tests).
+  // Retrieves the single Screen object; this may be null if it's not already
+  // created, except for IOS where it creates a native screen instance
+  // automatically.
   static Screen* GetScreen();
 
+  // Returns whether a Screen singleton exists or not.
+  static bool HasScreen();
+
+  // [Deprecated] as a public method. Do not use this.
   // Sets the global screen. Returns the previously installed screen, if any.
   // NOTE: this does not take ownership of |screen|. Tests must be sure to reset
   // any state they install.
-  static Screen* SetScreenInstance(Screen* instance);
+  static Screen* SetScreenInstance(Screen* instance,
+                                   const base::Location& location = FROM_HERE);
 
   // Returns the current absolute position of the mouse pointer.
   virtual gfx::Point GetCursorScreenPoint() = 0;
@@ -198,6 +206,8 @@
   virtual bool SetScreenSaverSuspended(bool suspend);
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 
+  void set_shutdown(bool shutdown) { shutdown_ = shutdown; }
+
  private:
   friend class ScopedDisplayForNewWindows;
 
@@ -208,6 +218,9 @@
 
   static gfx::NativeWindow GetWindowForView(gfx::NativeView view);
 
+  // A flag indicates that the instance is a special one used during shutdown.
+  bool shutdown_ = false;
+
   int64_t display_id_for_new_windows_;
   int64_t scoped_display_id_for_new_windows_ = display::kInvalidDisplayId;
 
@@ -216,7 +229,41 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX)
 };
 
-Screen* CreateNativeScreen();
+// TODO(crbug.com/1317416): Make this static private member of
+// ScopedNativeScreen.
+DISPLAY_EXPORT Screen* CreateNativeScreen();
+
+// Android does not have `CreateNativeScreen()`.
+#if !BUILDFLAG(IS_ANDROID)
+
+// ScopedNativeScreen creates a native screen if there is no screen created yet
+// (e.g. by a unit test).
+class DISPLAY_EXPORT ScopedNativeScreen {
+ public:
+  explicit ScopedNativeScreen(const base::Location& location = FROM_HERE);
+  ScopedNativeScreen(const ScopedNativeScreen&) = delete;
+  ScopedNativeScreen& operator=(const ScopedNativeScreen&) = delete;
+  virtual ~ScopedNativeScreen();
+
+  // Create and initialize the screen instance if the screen instance does not
+  // exist yet.
+  void MaybeInit(const base::Location& location = FROM_HERE);
+  void Shutdown();
+
+  Screen* screen() { return screen_.get(); }
+
+  virtual Screen* CreateScreen();
+
+ protected:
+  explicit ScopedNativeScreen(bool call_maybe_init,
+                              const base::Location& location = FROM_HERE);
+
+ private:
+  bool maybe_init_called_{false};
+  std::unique_ptr<Screen> screen_;
+};
+
+#endif
 
 }  // namespace display
 
diff --git a/ui/display/screen_base.cc b/ui/display/screen_base.cc
index 03d89f0..2e9e375 100644
--- a/ui/display/screen_base.cc
+++ b/ui/display/screen_base.cc
@@ -75,6 +75,10 @@
   display_list_.RemoveObserver(observer);
 }
 
+bool ScreenBase::HasDisplayObservers() const {
+  return !display_list_.observers()->empty();
+}
+
 void ScreenBase::SetPanelRotationForTesting(int64_t display_id,
                                             Display::Rotation rotation) {
   Display display = *display_list_.FindDisplayById(display_id);
diff --git a/ui/display/screen_base.h b/ui/display/screen_base.h
index 1b63863..ef3f7013 100644
--- a/ui/display/screen_base.h
+++ b/ui/display/screen_base.h
@@ -45,6 +45,8 @@
   void SetPanelRotationForTesting(int64_t display_id,
                                   Display::Rotation rotation) override;
 
+  bool HasDisplayObservers() const;
+
  protected:
   // Invoked when a display changed in some way, including being added.
   // If |is_primary| is true, |changed_display| is the primary display.
diff --git a/ui/display/test/scoped_screen_override.h b/ui/display/test/scoped_screen_override.h
index 0ba1c31..aba5307f 100644
--- a/ui/display/test/scoped_screen_override.h
+++ b/ui/display/test/scoped_screen_override.h
@@ -11,6 +11,8 @@
 
 namespace test {
 
+// [Deprecated] Do not use this in new code.
+//
 // This class represents a RAII wrapper for global screen overriding. An object
 // of this class restores original display::Screen instance when it goes out of
 // scope. Prefer to use it instead of directly call of
diff --git a/ui/display/test/test_screen.cc b/ui/display/test/test_screen.cc
index e2ae5bb5..af96e29 100644
--- a/ui/display/test/test_screen.cc
+++ b/ui/display/test/test_screen.cc
@@ -10,18 +10,33 @@
 
 namespace display {
 namespace test {
+namespace {
+TestScreen* test_screen = nullptr;
+}
 
 // static
 constexpr gfx::Rect TestScreen::kDefaultScreenBounds;
 
 TestScreen::TestScreen(bool create_display) {
+  DCHECK(!test_screen);
+  test_screen = this;
+
   if (!create_display)
     return;
   Display display(1, kDefaultScreenBounds);
   ProcessDisplayChanged(display, /* is_primary = */ true);
 }
 
-TestScreen::~TestScreen() {}
+TestScreen::~TestScreen() {
+  DCHECK_EQ(test_screen, this);
+  test_screen = nullptr;
+}
+
+// static
+TestScreen* TestScreen::Get() {
+  DCHECK_EQ(Screen::GetScreen(), test_screen);
+  return test_screen;
+}
 
 void TestScreen::set_cursor_screen_point(const gfx::Point& point) {
   cursor_screen_point_ = point;
diff --git a/ui/display/test/test_screen.h b/ui/display/test/test_screen.h
index ff7fe07..9962e07 100644
--- a/ui/display/test/test_screen.h
+++ b/ui/display/test/test_screen.h
@@ -21,6 +21,8 @@
  public:
   static constexpr gfx::Rect kDefaultScreenBounds = gfx::Rect(0, 0, 800, 600);
 
+  static TestScreen* Get();
+
   // TODO(weili): Split this into a protected no-argument constructor for
   // subclass uses and the public one with gfx::Size argument.
   explicit TestScreen(bool create_display = true);
diff --git a/ui/display/win/test/scoped_screen_win.cc b/ui/display/win/test/scoped_screen_win.cc
index 84c1b4ca..edb39b8c 100644
--- a/ui/display/win/test/scoped_screen_win.cc
+++ b/ui/display/win/test/scoped_screen_win.cc
@@ -20,10 +20,6 @@
         gfx::Vector2dF(96.0, 96.0), DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER}});
 }
 
-ScopedScreenWin::~ScopedScreenWin() {
-  Screen::SetScreenInstance(old_screen_);
-}
-
 }  // namespace test
 }  // namespace win
 }  // namespace display
diff --git a/ui/display/win/test/scoped_screen_win.h b/ui/display/win/test/scoped_screen_win.h
index ca6cb2c..0633d2e 100644
--- a/ui/display/win/test/scoped_screen_win.h
+++ b/ui/display/win/test/scoped_screen_win.h
@@ -12,6 +12,10 @@
 namespace win {
 namespace test {
 
+// [Deprecated]
+// TODO(crbug.com/1317416): The initialization code of this class should be
+// moved to the test that depends on it.
+//
 // ScopedScreenWin construct a instance of ScreenWinDisplay with bounds
 // (1920,1080). This will allow unittests to query the details about ScreenWin
 // using static methods. ScopedScreenWin needs to be initialized before running
@@ -23,10 +27,7 @@
   ScopedScreenWin(const ScopedScreenWin&) = delete;
   ScopedScreenWin& operator=(const ScopedScreenWin&) = delete;
 
-  ~ScopedScreenWin() override;
-
- private:
-  raw_ptr<Screen> old_screen_ = Screen::SetScreenInstance(this);
+  ~ScopedScreenWin() override = default;
 };
 
 }  // namespace test
diff --git a/ui/events/ozone/evdev/BUILD.gn b/ui/events/ozone/evdev/BUILD.gn
index e1f06d8..ccd75f0 100644
--- a/ui/events/ozone/evdev/BUILD.gn
+++ b/ui/events/ozone/evdev/BUILD.gn
@@ -142,6 +142,8 @@
 
   if (is_chromeos_ash) {
     sources += [
+      "fake_keyboard_heuristic_metrics.cc",
+      "fake_keyboard_heuristic_metrics.h",
       "numberpad_metrics.cc",
       "numberpad_metrics.h",
     ]
diff --git a/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc b/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc
new file mode 100644
index 0000000..5bef3c9b5
--- /dev/null
+++ b/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.cc
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h"
+
+#include "ui/events/ozone/features.h"
+
+namespace ui {
+
+FakeKeyboardHeuristicMetrics::FakeKeyboardHeuristicMetrics()
+    : feature_usage_metrics_("FakeKeyboardHeuristic", this) {}
+
+FakeKeyboardHeuristicMetrics::~FakeKeyboardHeuristicMetrics() = default;
+
+bool FakeKeyboardHeuristicMetrics::IsEligible() const {
+  return true;
+}
+
+bool FakeKeyboardHeuristicMetrics::IsEnabled() const {
+  return base::FeatureList::IsEnabled(kEnableFakeKeyboardHeuristic);
+}
+
+void FakeKeyboardHeuristicMetrics::RecordUsage(bool success) {
+  feature_usage_metrics_.RecordUsage(success);
+}
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h b/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h
new file mode 100644
index 0000000..b07112a9
--- /dev/null
+++ b/ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h
@@ -0,0 +1,26 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_
+#define UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_
+
+#include "chromeos/components/feature_usage/feature_usage_metrics.h"
+
+namespace ui {
+class FakeKeyboardHeuristicMetrics
+    : public feature_usage::FeatureUsageMetrics::Delegate {
+ public:
+  explicit FakeKeyboardHeuristicMetrics();
+  ~FakeKeyboardHeuristicMetrics() override;
+
+  bool IsEligible() const override;
+  bool IsEnabled() const override;
+  void RecordUsage(bool success);
+
+ private:
+  feature_usage::FeatureUsageMetrics feature_usage_metrics_;
+};
+}  // namespace ui
+
+#endif  // UI_EVENTS_OZONE_EVDEV_FAKE_KEYBOARD_HEURISTIC_METRICS_H_
diff --git a/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc b/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc
index 27de338..9d8461c2 100644
--- a/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc
+++ b/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.cc
@@ -44,6 +44,9 @@
   }
 
   converter->SetSuspectedImposter(true);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  fake_keyboard_heuristic_metrics_.RecordUsage(true);
+#endif
   return true;
 }
 
diff --git a/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h b/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h
index 6494059..871416c 100644
--- a/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h
+++ b/ui/events/ozone/evdev/keyboard_imposter_checker_evdev.h
@@ -11,6 +11,10 @@
 #include "ui/events/devices/input_device.h"
 #include "ui/events/ozone/evdev/event_converter_evdev.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ui/events/ozone/evdev/fake_keyboard_heuristic_metrics.h"
+#endif
+
 namespace ui {
 class COMPONENT_EXPORT(EVDEV) KeyboardImposterCheckerEvdev {
  public:
@@ -33,6 +37,10 @@
 
   // Number of devices per phys path.
   std::multimap<std::string, int> devices_on_phys_path_;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  FakeKeyboardHeuristicMetrics fake_keyboard_heuristic_metrics_;
+#endif
 };
 }  // namespace ui
 
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc
index 97cc367..5bb81b2 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.cc
@@ -14,7 +14,8 @@
 #define USE_EIGEN 0
 #endif
 
-namespace ui {
+namespace ui::internal_onedevice::alpha_model {
+
 namespace {
 
 // -----------------------------------------------------------------------------
@@ -3878,7 +3879,6 @@
                                                                          323};
 int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1};
 
-namespace internal_onedevice {
 void Inference(
     const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,323 */
     ,
@@ -3921,5 +3921,4 @@
 #endif
 }
 
-}  // namespace internal_onedevice
-}  // namespace ui
+}  // namespace ui::internal_onedevice::alpha_model
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h
index 02bb87d2..601d4af9 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference.h
@@ -6,8 +6,7 @@
 #define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_H_
 #include <cstdint>
 
-namespace ui {
-namespace internal_onedevice {
+namespace ui::internal_onedevice::alpha_model {
 struct alignas(16) FixedAllocations {
   float alloc0[20];
   int32_t shape0[2];
@@ -36,6 +35,5 @@
     ,
     FixedAllocations* __restrict fixed);
 
-}  // namespace internal_onedevice
-}  // namespace ui
+}  // namespace ui::internal_onedevice::alpha_model
 #endif  // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_H_
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc
index fd3add9..c431878 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.cc
@@ -14,7 +14,7 @@
 #define USE_EIGEN 0
 #endif
 
-namespace ui::v2 {
+namespace ui::internal_onedevice::alpha_model_v2 {
 namespace {
 
 // -----------------------------------------------------------------------------
@@ -15349,7 +15349,7 @@
 int32_t input_from_feature_columns_input_layer_concat_concat0Shape[2] = {1,
                                                                          173};
 int32_t logits_MatMul_merged_with_dnn_logits_BiasAdd0Shape[2] = {1, 1};
-namespace internal_onedevice {
+
 void Inference(
     const float* __restrict input_from_feature_columns_input_layer_concat_concat0 /* shape: 1,173 */
     ,
@@ -15419,5 +15419,4 @@
   Singleton<PerOpTimings>::get()->WriteTimingsToInfoLog();
 #endif
 }
-}  // namespace internal_onedevice
-}  // namespace ui::v2
+}  // namespace ui::internal_onedevice::alpha_model_v2
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h
index 733d0bd..97b5366 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_inference_v2.h
@@ -6,7 +6,7 @@
 #define UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_V2_H_
 #include <cstdint>
 
-namespace ui::v2::internal_onedevice {
+namespace ui::internal_onedevice::alpha_model_v2 {
 struct alignas(16) FixedAllocations {
   float alloc0[117];
   float alloc1[115];
@@ -36,5 +36,5 @@
     ,
     FixedAllocations* __restrict fixed);
 
-}  // namespace ui::v2::internal_onedevice
+}  // namespace ui::internal_onedevice::alpha_model_v2
 #endif  // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_MODEL_ONEDEVICE_TRAIN_PALM_DETECTION_FILTER_INFERENCE_V2_H_
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
index 6b65980..1c7e45c 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
@@ -25,6 +25,9 @@
 
 namespace ui {
 
+namespace alpha = internal_onedevice::alpha_model;
+namespace alpha_v2 = internal_onedevice::alpha_model_v2;
+
 float OneDeviceTrainNeuralStylusPalmDetectionFilterModel::Inference(
     const std::vector<float>& features) const {
   DVLOG(1) << "In Inference.";
@@ -41,15 +44,13 @@
   }
   float output = 0;
   if (base::FeatureList::IsEnabled(kEnableNeuralPalmRejectionModelV2)) {
-    std::unique_ptr<v2::internal_onedevice::FixedAllocations> fixed_allocations(
-        new v2::internal_onedevice::FixedAllocations());
-    v2::internal_onedevice::Inference(&features[0], &output,
-                                      fixed_allocations.get());
+    std::unique_ptr<alpha_v2::FixedAllocations> fixed_allocations(
+        new alpha_v2::FixedAllocations());
+    alpha_v2::Inference(&features[0], &output, fixed_allocations.get());
   } else {
-    std::unique_ptr<internal_onedevice::FixedAllocations> fixed_allocations(
-        new internal_onedevice::FixedAllocations());
-    internal_onedevice::Inference(&features[0], &output,
-                                  fixed_allocations.get());
+    std::unique_ptr<alpha::FixedAllocations> fixed_allocations(
+        new alpha::FixedAllocations());
+    alpha::Inference(&features[0], &output, fixed_allocations.get());
   }
   return output;
 }
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
index 3aeef16..f14c339 100644
--- a/ui/file_manager/integration_tests/file_manager/zip_files.js
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -15,6 +15,12 @@
 const ZipCreationTimeHistogramName = 'FileBrowser.ZipTask.Time';
 
 /**
+ * The name of the UMA to track extract archive status.
+ * @const {string}
+ */
+const ExtractArchiveStatusHistogramName = 'FileBrowser.ExtractTask.Status';
+
+/**
  * Returns the expected file list row entries after opening (mounting) the
  * ENTRIES.zipArchive file list entry.
  */
@@ -349,6 +355,9 @@
         caller,
         `Expected feedback panel msg: "${expectedMsg}", got "${actualMsg}"`);
   });
+
+  // Check: a extract archive status histogram value should have been recorded.
+  await expectHistogramTotalCount(ExtractArchiveStatusHistogramName, 1);
 };
 
 /**
@@ -507,6 +516,9 @@
   await remoteCall.waitForElement(appId, '#file-list [file-name="folder"]');
   await remoteCall.waitForElement(appId, '#file-list [file-name="text.txt"]');
   await remoteCall.waitForElement(appId, '#file-list [file-name="image.png"]');
+
+  // Check: a extract archive status histogram value should have been recorded.
+  await expectHistogramTotalCount(ExtractArchiveStatusHistogramName, 1);
 };
 
 /**
@@ -570,6 +582,9 @@
   await remoteCall.waitForElement(appId, '#file-list [file-name="folder"]');
   await remoteCall.waitForElement(appId, '#file-list [file-name="text.txt"]');
   await remoteCall.waitForElement(appId, '#file-list [file-name="image.png"]');
+
+  // Check: 2 extract archive status histogram value should have been recorded.
+  await expectHistogramTotalCount(ExtractArchiveStatusHistogramName, 2);
 };
 
 /**
@@ -614,6 +629,9 @@
   // Check: File content in the ZIP with decoded name should appear.
   await remoteCall.waitForElement(
       appId, '#file-list [file-name="新しいフォルダ"]');
+
+  // Check: a extract archive status histogram value should have been recorded.
+  await expectHistogramTotalCount(ExtractArchiveStatusHistogramName, 1);
 };
 
 /**
@@ -689,4 +707,7 @@
         caller,
         `Expected feedback panel msg: "${expectedMsg}", got "${actualMsg}"`);
   });
+
+  // Check: a extract archive status histogram value should have been recorded.
+  await expectHistogramTotalCount(ExtractArchiveStatusHistogramName, 1);
 };
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 626e9ebf..1ab4bd9 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -163,6 +163,8 @@
     "host/wayland_window_manager.h",
     "host/wayland_window_observer.cc",
     "host/wayland_window_observer.h",
+    "host/wayland_zaura_output.cc",
+    "host/wayland_zaura_output.h",
     "host/wayland_zaura_shell.cc",
     "host/wayland_zaura_shell.h",
     "host/wayland_zcr_cursor_shapes.cc",
@@ -468,6 +470,8 @@
     "test/test_wp_pointer_gestures.h",
     "test/test_xdg_popup.cc",
     "test/test_xdg_popup.h",
+    "test/test_zaura_output.cc",
+    "test/test_zaura_output.h",
     "test/test_zcr_text_input_extension.cc",
     "test/test_zcr_text_input_extension.h",
     "test/test_zwp_linux_buffer_params.cc",
@@ -529,6 +533,7 @@
     "host/wayland_window_drag_controller_unittest.cc",
     "host/wayland_window_manager_unittests.cc",
     "host/wayland_window_unittest.cc",
+    "host/wayland_zaura_output_unittest.cc",
     "host/wayland_zaura_shell_unittest.cc",
     "host/wayland_zwp_pointer_gestures_unittest.cc",
     "test/wayland_drag_drop_test.cc",
diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc
index b7625c6..580491c 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -167,6 +167,7 @@
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_surface)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_toplevel)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(xdg_wm_base)
+IMPLEMENT_WAYLAND_OBJECT_TRAITS(zaura_output)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(zaura_shell)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(zaura_surface)
 IMPLEMENT_WAYLAND_OBJECT_TRAITS(zaura_toplevel)
diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h
index ef28758..3d9e53a 100644
--- a/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/ui/ozone/platform/wayland/common/wayland_object.h
@@ -148,6 +148,7 @@
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_surface)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_toplevel)
 DECLARE_WAYLAND_OBJECT_TRAITS(xdg_wm_base)
+DECLARE_WAYLAND_OBJECT_TRAITS(zaura_output)
 DECLARE_WAYLAND_OBJECT_TRAITS(zaura_shell)
 DECLARE_WAYLAND_OBJECT_TRAITS(zaura_surface)
 DECLARE_WAYLAND_OBJECT_TRAITS(zaura_toplevel)
diff --git a/ui/ozone/platform/wayland/host/wayland_output.cc b/ui/ozone/platform/wayland/host/wayland_output.cc
index 2186d6a..01a01b2 100644
--- a/ui/ozone/platform/wayland/host/wayland_output.cc
+++ b/ui/ozone/platform/wayland/host/wayland_output.cc
@@ -4,6 +4,7 @@
 
 #include "ui/ozone/platform/wayland/host/wayland_output.h"
 
+#include <aura-shell-client-protocol.h>
 #include <xdg-output-unstable-v1-client-protocol.h>
 
 #include "base/logging.h"
@@ -11,6 +12,7 @@
 #include "ui/gfx/color_space.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_zaura_output.h"
 #include "ui/ozone/platform/wayland/host/xdg_output.h"
 
 namespace ui {
@@ -66,6 +68,12 @@
       zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output_.get()));
 }
 
+void WaylandOutput::InitializeZAuraOutput(zaura_shell* aura_shell) {
+  DCHECK(!aura_output_);
+  aura_output_ = std::make_unique<WaylandZAuraOutput>(
+      zaura_shell_get_aura_output(aura_shell, output_.get()));
+}
+
 void WaylandOutput::Initialize(Delegate* delegate) {
   DCHECK(!delegate_);
   delegate_ = delegate;
@@ -84,6 +92,14 @@
              : scale_factor();
 }
 
+gfx::Insets WaylandOutput::insets() const {
+  return aura_output_ ? aura_output_->insets() : gfx::Insets();
+}
+
+zaura_output* WaylandOutput::get_zaura_output() const {
+  return aura_output_ ? aura_output_->wl_object() : nullptr;
+}
+
 void WaylandOutput::TriggerDelegateNotifications() {
   if (xdg_output_ && connection_->surface_submission_in_pixel_coordinates()) {
     DCHECK(!rect_in_physical_pixels_.IsEmpty());
@@ -99,7 +115,7 @@
     }
   }
   delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_,
-                                   scale_factor_, transform_);
+                                   insets(), scale_factor_, transform_);
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_output.h b/ui/ozone/platform/wayland/host/wayland_output.h
index c36c7c7..ce225ac 100644
--- a/ui/ozone/platform/wayland/host/wayland_output.h
+++ b/ui/ozone/platform/wayland/host/wayland_output.h
@@ -16,6 +16,7 @@
 
 class XDGOutput;
 class WaylandConnection;
+class WaylandZAuraOutput;
 
 // WaylandOutput objects keep track of the current output of display
 // that are available to the application.
@@ -33,6 +34,7 @@
    public:
     virtual void OnOutputHandleMetrics(uint32_t output_id,
                                        const gfx::Rect& new_bounds,
+                                       const gfx::Insets& insets,
                                        float scale_factor,
                                        int32_t transform) = 0;
 
@@ -51,6 +53,7 @@
 
   void Initialize(Delegate* delegate);
   void InitializeXdgOutput(struct zxdg_output_manager_v1* manager);
+  void InitializeZAuraOutput(zaura_shell* aura_shell);
   float GetUIScaleFactor() const;
 
   uint32_t output_id() const { return output_id_; }
@@ -58,12 +61,14 @@
   float scale_factor() const { return scale_factor_; }
   int32_t transform() const { return transform_; }
   gfx::Rect bounds() const { return rect_in_physical_pixels_; }
+  gfx::Insets insets() const;
 
   // Tells if the output has already received physical screen dimensions in the
   // global compositor space.
   bool is_ready() const { return !rect_in_physical_pixels_.IsEmpty(); }
 
-  wl_output* get_output() { return output_.get(); }
+  wl_output* get_output() const { return output_.get(); }
+  zaura_output* get_zaura_output() const;
 
  private:
   static constexpr int32_t kDefaultScaleFactor = 1;
@@ -97,6 +102,7 @@
   const uint32_t output_id_ = 0;
   wl::Object<wl_output> output_;
   std::unique_ptr<XDGOutput> xdg_output_;
+  std::unique_ptr<WaylandZAuraOutput> aura_output_;
   float scale_factor_ = kDefaultScaleFactor;
   int32_t transform_ = WL_OUTPUT_TRANSFORM_NORMAL;
   gfx::Rect rect_in_physical_pixels_;
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.cc b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
index a7ccee03..432bc96d 100644
--- a/ui/ozone/platform/wayland/host/wayland_output_manager.cc
+++ b/ui/ozone/platform/wayland/host/wayland_output_manager.cc
@@ -11,6 +11,7 @@
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_output.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_zaura_shell.h"
 
 namespace ui {
 
@@ -45,6 +46,10 @@
   wayland_output->Initialize(this);
   if (connection_->xdg_output_manager_v1())
     wayland_output->InitializeXdgOutput(connection_->xdg_output_manager_v1());
+  if (connection_->zaura_shell()) {
+    wayland_output->InitializeZAuraOutput(
+        connection_->zaura_shell()->wl_object());
+  }
   DCHECK(!wayland_output->is_ready());
 
   output_list_[output_id] = std::move(wayland_output);
@@ -74,6 +79,14 @@
     output.second->InitializeXdgOutput(connection_->xdg_output_manager_v1());
 }
 
+void WaylandOutputManager::InitializeAllZAuraOutputs() {
+  DCHECK(connection_->zaura_shell());
+  for (const auto& output : output_list_) {
+    output.second->InitializeZAuraOutput(
+        connection_->zaura_shell()->wl_object());
+  }
+}
+
 std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen() {
   auto wayland_screen = std::make_unique<WaylandScreen>(connection_);
   wayland_screen_ = wayland_screen->GetWeakPtr();
@@ -94,7 +107,8 @@
     if (output.second->is_ready()) {
       screen->OnOutputAddedOrUpdated(
           output.second->output_id(), output.second->bounds(),
-          output.second->scale_factor(), output.second->transform());
+          output.second->insets(), output.second->scale_factor(),
+          output.second->transform());
     }
   }
 }
@@ -115,10 +129,11 @@
 
 void WaylandOutputManager::OnOutputHandleMetrics(uint32_t output_id,
                                                  const gfx::Rect& new_bounds,
+                                                 const gfx::Insets& insets,
                                                  float scale_factor,
                                                  int32_t transform) {
   if (wayland_screen_) {
-    wayland_screen_->OnOutputAddedOrUpdated(output_id, new_bounds,
+    wayland_screen_->OnOutputAddedOrUpdated(output_id, new_bounds, insets,
                                             scale_factor, transform);
   }
   auto* wayland_window_manager = connection_->wayland_window_manager();
diff --git a/ui/ozone/platform/wayland/host/wayland_output_manager.h b/ui/ozone/platform/wayland/host/wayland_output_manager.h
index 9f5711b4..49e2245 100644
--- a/ui/ozone/platform/wayland/host/wayland_output_manager.h
+++ b/ui/ozone/platform/wayland/host/wayland_output_manager.h
@@ -38,6 +38,7 @@
   void RemoveWaylandOutput(const uint32_t output_id);
 
   void InitializeAllXdgOutputs();
+  void InitializeAllZAuraOutputs();
 
   // Creates a platform screen.
   std::unique_ptr<WaylandScreen> CreateWaylandScreen();
@@ -54,6 +55,7 @@
   // WaylandOutput::Delegate:
   void OnOutputHandleMetrics(uint32_t output_id,
                              const gfx::Rect& new_bounds,
+                             const gfx::Insets& insets,
                              float scale_factor,
                              int32_t transform) override;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc
index c574965..4f5b2ae 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -109,9 +109,10 @@
 
 void WaylandScreen::OnOutputAddedOrUpdated(uint32_t output_id,
                                            const gfx::Rect& bounds,
+                                           const gfx::Insets& insets,
                                            float scale,
                                            int32_t transform) {
-  AddOrUpdateDisplay(output_id, bounds, scale, transform);
+  AddOrUpdateDisplay(output_id, bounds, insets, scale, transform);
 }
 
 void WaylandScreen::OnOutputRemoved(uint32_t output_id) {
@@ -142,15 +143,12 @@
 
 void WaylandScreen::AddOrUpdateDisplay(uint32_t output_id,
                                        const gfx::Rect& new_bounds,
+                                       const gfx::Insets& insets,
                                        float scale_factor,
                                        int32_t transform) {
   display::Display changed_display(output_id);
-  if (!display::Display::HasForceDeviceScaleFactor()) {
-    changed_display.SetScaleAndBounds(scale_factor, new_bounds);
-  } else {
-    changed_display.set_bounds(new_bounds);
-    changed_display.set_work_area(new_bounds);
-  }
+  changed_display.SetScaleAndBounds(scale_factor, new_bounds);
+  changed_display.UpdateWorkAreaFromInsets(insets);
 
   DCHECK_GE(transform, WL_OUTPUT_TRANSFORM_NORMAL);
   DCHECK_LE(transform, WL_OUTPUT_TRANSFORM_FLIPPED_270);
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.h b/ui/ozone/platform/wayland/host/wayland_screen.h
index 5b59b77..da79707 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.h
+++ b/ui/ozone/platform/wayland/host/wayland_screen.h
@@ -41,6 +41,7 @@
 
   void OnOutputAddedOrUpdated(uint32_t output_id,
                               const gfx::Rect& bounds,
+                              const gfx::Insets& insets,
                               float output_scale,
                               int32_t output_transform);
   void OnOutputRemoved(uint32_t output_id);
@@ -73,8 +74,11 @@
       const gfx::GpuExtraInfo& gpu_extra_info) override;
 
  private:
+  // |bounds| is given in physical pixel coordinates.
+  // |insets| is given in DIP screen coordinates.
   void AddOrUpdateDisplay(uint32_t output_id,
                           const gfx::Rect& bounds,
+                          const gfx::Insets& insets,
                           float scale,
                           int32_t transform);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
index ca80363..343e01a 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -21,6 +21,7 @@
 #include "ui/ozone/platform/wayland/test/mock_pointer.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/mock_wayland_platform_window_delegate.h"
+#include "ui/ozone/platform/wayland/test/mock_zaura_shell.h"
 #include "ui/ozone/platform/wayland/test/test_output.h"
 #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/test/wayland_test.h"
@@ -90,6 +91,9 @@
 
     WaylandTest::SetUp();
 
+    mock_zaura_shell_ = std::make_unique<wl::MockZAuraShell>();
+    mock_zaura_shell_->Initialize(server_.display());
+
     output_->SetRect({kOutputWidth, kOutputHeight});
     output_->SetScale(1);
     output_->Flush();
@@ -124,6 +128,8 @@
     EXPECT_EQ(display_for_widget.id(), expected_display_id);
   }
 
+  std::unique_ptr<wl::MockZAuraShell> mock_zaura_shell_;
+
   wl::TestOutput* output_ = nullptr;
   WaylandOutputManager* output_manager_ = nullptr;
 
@@ -295,7 +301,7 @@
   TestDisplayObserver observer;
   platform_screen_->AddObserver(&observer);
 
-  gfx::Rect new_rect{100, 100};
+  const gfx::Rect new_rect{100, 100};
   output_->SetRect(new_rect);
   output_->Flush();
 
@@ -305,7 +311,24 @@
                             display::DisplayObserver::DISPLAY_METRIC_WORK_AREA;
   EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
   EXPECT_EQ(observer.GetDisplay().bounds(), new_rect);
+  EXPECT_EQ(observer.GetDisplay().work_area(), new_rect);
 
+  // Test work area.
+  const gfx::Rect new_work_area{80, 80};
+  ASSERT_TRUE(output_->GetAuraOutput());
+  output_->GetAuraOutput()->SetInsets(new_rect.InsetsFrom(new_work_area));
+  output_->Flush();
+
+  Sync();
+
+  changed_values = display::DisplayObserver::DISPLAY_METRIC_WORK_AREA;
+  EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
+  // Bounds should be unchanged.
+  EXPECT_EQ(observer.GetDisplay().bounds(), new_rect);
+  // Work area should have new value.
+  EXPECT_EQ(observer.GetDisplay().work_area(), new_work_area);
+
+  // Test scaling.
   const int32_t new_scale_value = 2;
   output_->SetScale(new_scale_value);
   output_->Flush();
@@ -319,6 +342,7 @@
   EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values);
   EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value);
   EXPECT_EQ(observer.GetDisplay().bounds(), gfx::Rect(50, 50));
+  EXPECT_EQ(observer.GetDisplay().work_area(), gfx::Rect(30, 30));
 
   platform_screen_->RemoveObserver(&observer);
 }
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output.cc b/ui/ozone/platform/wayland/host/wayland_zaura_output.cc
new file mode 100644
index 0000000..4df501f
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_output.cc
@@ -0,0 +1,47 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_zaura_output.h"
+
+#include <aura-shell-client-protocol.h>
+
+#include "base/check.h"
+
+namespace ui {
+
+WaylandZAuraOutput::WaylandZAuraOutput(zaura_output* aura_output)
+    : obj_(aura_output) {
+  DCHECK(obj_);
+
+  static constexpr zaura_output_listener kZAuraOutputListener = {
+      &OnScale, &OnConnection, &OnDeviceScaleFactor, &OnInsets};
+  zaura_output_add_listener(obj_.get(), &kZAuraOutputListener, this);
+}
+
+WaylandZAuraOutput::~WaylandZAuraOutput() = default;
+
+void WaylandZAuraOutput::OnScale(void* data,
+                                 struct zaura_output* zaura_output,
+                                 uint32_t flags,
+                                 uint32_t scale) {}
+
+void WaylandZAuraOutput::OnConnection(void* data,
+                                      struct zaura_output* zaura_output,
+                                      uint32_t connection) {}
+
+void WaylandZAuraOutput::OnDeviceScaleFactor(void* data,
+                                             struct zaura_output* zaura_output,
+                                             uint32_t scale) {}
+
+void WaylandZAuraOutput::OnInsets(void* data,
+                                  struct zaura_output* zaura_output,
+                                  int32_t top,
+                                  int32_t left,
+                                  int32_t bottom,
+                                  int32_t right) {
+  if (auto* aura_output = static_cast<WaylandZAuraOutput*>(data))
+    aura_output->insets_ = gfx::Insets::TLBR(top, left, bottom, right);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output.h b/ui/ozone/platform/wayland/host/wayland_zaura_output.h
new file mode 100644
index 0000000..ba813cbf
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_output.h
@@ -0,0 +1,52 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZAURA_OUTPUT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZAURA_OUTPUT_H_
+
+#include <cstdint>
+
+#include "ui/gfx/geometry/insets.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
+
+namespace ui {
+
+// Wraps the zaura_output object.
+class WaylandZAuraOutput {
+ public:
+  explicit WaylandZAuraOutput(zaura_output* aura_output);
+  WaylandZAuraOutput(const WaylandZAuraOutput&) = delete;
+  WaylandZAuraOutput& operator=(const WaylandZAuraOutput&) = delete;
+  ~WaylandZAuraOutput();
+
+  zaura_output* wl_object() { return obj_.get(); }
+
+  const gfx::Insets& insets() const { return insets_; }
+
+ private:
+  // zaura_output_listeners
+  static void OnScale(void* data,
+                      struct zaura_output* zaura_output,
+                      uint32_t flags,
+                      uint32_t scale);
+  static void OnConnection(void* data,
+                           struct zaura_output* zaura_output,
+                           uint32_t connection);
+  static void OnDeviceScaleFactor(void* data,
+                                  struct zaura_output* zaura_output,
+                                  uint32_t scale);
+  static void OnInsets(void* data,
+                       struct zaura_output* zaura_output,
+                       int32_t top,
+                       int32_t left,
+                       int32_t bottom,
+                       int32_t right);
+
+  wl::Object<zaura_output> obj_;
+  gfx::Insets insets_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_ZAURA_OUTPUT_H_
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc b/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc
new file mode 100644
index 0000000..b26a72c7
--- /dev/null
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_output_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_zaura_output.h"
+
+#include <components/exo/wayland/protocol/aura-shell-client-protocol.h>
+#include <components/exo/wayland/protocol/aura-shell-server-protocol.h>
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_output.h"
+#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
+#include "ui/ozone/platform/wayland/test/mock_zaura_shell.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
+#include "ui/ozone/platform/wayland/test/test_zaura_output.h"
+
+namespace ui {
+namespace {
+
+using ::testing::Values;
+
+class WaylandZAuraOutputTest : public ::testing::Test {
+ public:
+  WaylandZAuraOutputTest()
+      : task_environment_(
+            base::test::SingleThreadTaskEnvironment::MainThreadType::UI) {}
+
+  WaylandZAuraOutputTest(const WaylandZAuraOutputTest&) = delete;
+  WaylandZAuraOutputTest& operator=(const WaylandZAuraOutputTest&) = delete;
+
+  ~WaylandZAuraOutputTest() override = default;
+
+  void SetUp() override {
+    ::testing::Test::SetUp();
+
+    ASSERT_TRUE(server_.Start({.shell_version = wl::ShellVersion::kStable}));
+    mock_zaura_shell_.Initialize(server_.display());
+
+    ASSERT_TRUE(connection_.Initialize());
+    connection_.event_source()->StartProcessingEvents();
+    base::RunLoop().RunUntilIdle();
+
+    // Set default values for the output.
+    wl::TestOutput* output = server_.output();
+    output->SetRect({800, 600});
+    output->SetScale(1);
+    output->Flush();
+
+    base::RunLoop().RunUntilIdle();
+    server_.Pause();
+
+    output_manager_ = connection_.wayland_output_manager();
+    ASSERT_TRUE(output_manager_);
+    EXPECT_TRUE(output_manager_->IsOutputReady());
+
+    // Initializing the screen also connects it to the primary output, so it's
+    // easier for us to get the associated WaylandOutput object later.
+    platform_screen_ = output_manager_->CreateWaylandScreen();
+    output_manager_->InitWaylandScreen(platform_screen_.get());
+  }
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  wl::TestWaylandServerThread server_;
+  wl::MockZAuraShell mock_zaura_shell_;
+  WaylandConnection connection_;
+
+  WaylandOutputManager* output_manager_ = nullptr;
+  std::unique_ptr<WaylandScreen> platform_screen_;
+};
+
+TEST_F(WaylandZAuraOutputTest, HandleInsets) {
+  WaylandOutput* wayland_output = output_manager_->GetPrimaryOutput();
+  ASSERT_TRUE(wayland_output);
+  EXPECT_TRUE(wayland_output->is_ready());
+  EXPECT_EQ(wayland_output->bounds(), gfx::Rect(800, 600));
+  EXPECT_TRUE(wayland_output->insets().IsEmpty());
+  EXPECT_TRUE(wayland_output->get_zaura_output());
+
+  // Simulate server sending updated insets to the client.
+  wl_resource* zaura_output_resource =
+      server_.output()->GetAuraOutput()->resource();
+  ASSERT_TRUE(zaura_output_resource);
+  const gfx::Insets sent_insets =
+      gfx::Rect(800, 600).InsetsFrom(gfx::Rect(10, 10, 500, 400));
+  EXPECT_FALSE(sent_insets.IsEmpty());
+  zaura_output_send_insets(zaura_output_resource, sent_insets.top(),
+                           sent_insets.left(), sent_insets.bottom(),
+                           sent_insets.right());
+
+  server_.Resume();
+  base::RunLoop().RunUntilIdle();
+  server_.Pause();
+
+  // Verify that insets is updated.
+  EXPECT_TRUE(wayland_output->is_ready());
+  EXPECT_EQ(wayland_output->bounds(), gfx::Rect(800, 600));
+  EXPECT_EQ(wayland_output->insets(), sent_insets);
+}
+
+}  // namespace
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
index 5114ddf..5532e22 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -20,7 +20,7 @@
 
 namespace {
 constexpr uint32_t kMinVersion = 1;
-constexpr uint32_t kMaxVersion = 31;
+constexpr uint32_t kMaxVersion = 32;
 }
 
 // static
@@ -48,6 +48,13 @@
   connection->zaura_shell_ =
       std::make_unique<WaylandZAuraShell>(zaura_shell.release(), connection);
   ReportShellUMA(UMALinuxWaylandShell::kZauraShell);
+
+  // Usually WaylandOutputManager is instantiated first, so any ZAuraOutputs it
+  // created wouldn't have been initialized, since the zaura_shell didn't exist
+  // yet. So initialize them now.
+  if (connection->wayland_output_manager()) {
+    connection->wayland_output_manager()->InitializeAllZAuraOutputs();
+  }
 }
 
 WaylandZAuraShell::WaylandZAuraShell(zaura_shell* aura_shell,
diff --git a/ui/ozone/platform/wayland/test/mock_zaura_shell.cc b/ui/ozone/platform/wayland/test/mock_zaura_shell.cc
index 7e5fae6..0f2c508 100644
--- a/ui/ozone/platform/wayland/test/mock_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/test/mock_zaura_shell.cc
@@ -5,12 +5,15 @@
 #include "ui/ozone/platform/wayland/test/mock_zaura_shell.h"
 
 #include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_output.h"
+#include "ui/ozone/platform/wayland/test/test_zaura_output.h"
 
 namespace wl {
 
 namespace {
 
-constexpr uint32_t kZAuraShellVersion = 26;
+constexpr uint32_t kZAuraShellVersion = 32;
+constexpr uint32_t kZAuraOutputVersion = 32;
 
 void GetAuraSurface(wl_client* client,
                     wl_resource* resource,
@@ -20,7 +23,12 @@
 void GetAuraOutput(wl_client* client,
                    wl_resource* resource,
                    uint32_t id,
-                   wl_resource* output_resource) {}
+                   wl_resource* output_resource) {
+  wl_resource* zaura_output_resource = CreateResourceWithImpl<TestZAuraOutput>(
+      client, &zaura_output_interface, kZAuraOutputVersion, nullptr, id);
+  auto* output = GetUserDataAs<TestOutput>(output_resource);
+  output->SetAuraOutput(GetUserDataAs<TestZAuraOutput>(zaura_output_resource));
+}
 
 void SurfaceSubmissionInPixelCoordinates(wl_client* client,
                                          wl_resource* resource) {}
diff --git a/ui/ozone/platform/wayland/test/test_output.cc b/ui/ozone/platform/wayland/test/test_output.cc
index 3d8b0d2..c0ec7a90 100644
--- a/ui/ozone/platform/wayland/test/test_output.cc
+++ b/ui/ozone/platform/wayland/test/test_output.cc
@@ -56,6 +56,10 @@
     scale_ = std::move(pending_scale_.value());
     wl_output_send_scale(resource(), scale_);
   }
+
+  if (aura_output_)
+    aura_output_->Flush();
+
   wl_output_send_done(resource());
 }
 
@@ -71,4 +75,12 @@
   Flush();
 }
 
+void TestOutput::SetAuraOutput(TestZAuraOutput* aura_output) {
+  aura_output_ = aura_output;
+}
+
+TestZAuraOutput* TestOutput::GetAuraOutput() {
+  return aura_output_;
+}
+
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_output.h b/ui/ozone/platform/wayland/test/test_output.h
index b8ed75c8..5b90dc0 100644
--- a/ui/ozone/platform/wayland/test/test_output.h
+++ b/ui/ozone/platform/wayland/test/test_output.h
@@ -11,6 +11,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/ozone/platform/wayland/test/global_object.h"
+#include "ui/ozone/platform/wayland/test/test_zaura_output.h"
 
 namespace wl {
 
@@ -32,6 +33,9 @@
 
   void Flush();
 
+  void SetAuraOutput(TestZAuraOutput* aura_output);
+  TestZAuraOutput* GetAuraOutput();
+
  protected:
   void OnBind() override;
 
@@ -43,6 +47,8 @@
   absl::optional<gfx::Rect> pending_rect_ = absl::nullopt;
   absl::optional<int32_t> pending_scale_ = absl::nullopt;
   absl::optional<wl_output_transform> pending_transform_ = absl::nullopt;
+
+  TestZAuraOutput* aura_output_ = nullptr;
 };
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_zaura_output.cc b/ui/ozone/platform/wayland/test/test_zaura_output.cc
new file mode 100644
index 0000000..afcead2
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_zaura_output.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/test_zaura_output.h"
+
+#include <aura-shell-server-protocol.h>
+
+namespace wl {
+
+TestZAuraOutput::TestZAuraOutput(wl_resource* resource)
+    : ServerObject(resource) {}
+
+TestZAuraOutput::~TestZAuraOutput() = default;
+
+void TestZAuraOutput::Flush() {
+  if (pending_insets_) {
+    insets_ = std::move(*pending_insets_);
+    pending_insets_.reset();
+    zaura_output_send_insets(resource(), insets_.top(), insets_.left(),
+                             insets_.bottom(), insets_.right());
+  }
+}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_zaura_output.h b/ui/ozone/platform/wayland/test/test_zaura_output.h
new file mode 100644
index 0000000..9aabb5b5
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_zaura_output.h
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZAURA_OUTPUT_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZAURA_OUTPUT_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+namespace wl {
+
+// Manages zaura_output object.
+class TestZAuraOutput : public ServerObject {
+ public:
+  explicit TestZAuraOutput(wl_resource* resource);
+
+  TestZAuraOutput(const TestZAuraOutput&) = delete;
+  TestZAuraOutput& operator=(const TestZAuraOutput&) = delete;
+
+  ~TestZAuraOutput() override;
+
+  const gfx::Insets& GetInsets() const { return insets_; }
+  void SetInsets(const gfx::Insets& insets) { pending_insets_ = insets; }
+
+  void Flush();
+
+ private:
+  gfx::Insets insets_;
+  absl::optional<gfx::Insets> pending_insets_;
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_ZAURA_OUTPUT_H_
diff --git a/ui/ozone/platform/x11/BUILD.gn b/ui/ozone/platform/x11/BUILD.gn
index b1180a8..252986e 100644
--- a/ui/ozone/platform/x11/BUILD.gn
+++ b/ui/ozone/platform/x11/BUILD.gn
@@ -151,6 +151,7 @@
     "//ui/base/x",
     "//ui/base/x:test_support",
     "//ui/base/x:unittests",
+    "//ui/display:test_support",
     "//ui/events:test_support",
     "//ui/events/devices/x11",
     "//ui/events/platform/x11",
diff --git a/ui/ozone/platform/x11/test/x11_window_unittest.cc b/ui/ozone/platform/x11/test/x11_window_unittest.cc
index f499dbe..cccb2e24 100644
--- a/ui/ozone/platform/x11/test/x11_window_unittest.cc
+++ b/ui/ozone/platform/x11/test/x11_window_unittest.cc
@@ -15,6 +15,7 @@
 #include "ui/base/x/x11_util.h"
 #include "ui/display/display_switches.h"
 #include "ui/display/screen_base.h"
+#include "ui/display/test/test_screen.h"
 #include "ui/events/devices/x11/touch_factory_x11.h"
 #include "ui/events/event.h"
 #include "ui/events/platform/x11/x11_event_source.h"
@@ -215,8 +216,8 @@
     ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(pointer_devices);
 
     // X11 requires display::Screen instance.
-    test_screen_ = new TestScreen();
-    display::Screen::SetScreenInstance(test_screen_);
+    test_screen_.emplace();
+    display::Screen::SetScreenInstance(&test_screen_.value());
 
     // Make X11 synchronous for our display connection. This does not force the
     // window manager to behave synchronously.
@@ -226,6 +227,8 @@
  protected:
   void TearDown() override {
     x11::Connection::Get()->SynchronizeForTest(false);
+    display::Screen::SetScreenInstance(nullptr);
+    test_screen_.reset();
   }
 
   std::unique_ptr<X11Window> CreateX11Window(
@@ -252,7 +255,7 @@
   std::unique_ptr<base::test::TaskEnvironment> task_env_;
   std::unique_ptr<X11EventSource> event_source_;
 
-  TestScreen* test_screen_ = nullptr;
+  absl::optional<TestScreen> test_screen_;
 };
 
 // https://crbug.com/898742: Test is flaky.
diff --git a/ui/ozone/platform/x11/x11_window_ozone_unittest.cc b/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
index f130507c..8f8953c 100644
--- a/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
+++ b/ui/ozone/platform/x11/x11_window_ozone_unittest.cc
@@ -78,12 +78,13 @@
   void SetUp() override {
     event_source_ = std::make_unique<X11EventSource>(x11::Connection::Get());
 
-    test_screen_ = new TestScreen();
-    display::Screen::SetScreenInstance(test_screen_);
+    display::Screen::SetScreenInstance(&test_screen_);
 
     TouchFactory::GetInstance()->SetPointerDeviceForTest({kPointerDeviceId});
   }
 
+  void TearDown() override { display::Screen::SetScreenInstance(nullptr); }
+
  protected:
   std::unique_ptr<PlatformWindow> CreatePlatformWindow(
       MockPlatformWindowDelegate* delegate,
@@ -112,7 +113,7 @@
     return window_manager;
   }
 
-  TestScreen* test_screen_ = nullptr;
+  TestScreen test_screen_;
 
  private:
   std::unique_ptr<base::test::TaskEnvironment> task_env_;
@@ -283,7 +284,7 @@
 // Verifies X11Window sets fullscreen bounds in pixels when going to fullscreen.
 TEST_F(X11WindowOzoneTest, ToggleFullscreen) {
   constexpr gfx::Rect screen_bounds_in_px(640, 480, 1280, 720);
-  test_screen_->SetScaleAndBoundsForPrimaryDisplay(2, screen_bounds_in_px);
+  test_screen_.SetScaleAndBoundsForPrimaryDisplay(2, screen_bounds_in_px);
 
   MockPlatformWindowDelegate delegate;
   gfx::AcceleratedWidget widget;
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index aa32ca5..30fcf86d 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -97,6 +97,11 @@
 }
 
 // static
+bool OzonePlatform::IsInitialized() {
+  return !!g_instance;
+}
+
+// static
 std::string OzonePlatform::GetPlatformNameForTest() {
   return GetOzonePlatformName();
 }
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 22ba323..df6b551 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -240,6 +240,8 @@
 
   static OzonePlatform* GetInstance();
 
+  static bool IsInitialized();
+
   // Returns the current ozone platform name.
   // Some tests may skip based on the platform name.
   static std::string GetPlatformNameForTest();
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index f786b1b..45189c7 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -820,30 +820,33 @@
     : public BubbleDialogDelegateViewTest,
       public testing::WithParamInterface<ArrowTestParameters> {
  public:
-  BubbleDialogDelegateViewArrowTest() : screen_override_(SetUpTestScreen()) {}
+  BubbleDialogDelegateViewArrowTest() { SetUpTestScreen(); }
 
   BubbleDialogDelegateViewArrowTest(const BubbleDialogDelegateViewArrowTest&) =
       delete;
   BubbleDialogDelegateViewArrowTest& operator=(
       const BubbleDialogDelegateViewArrowTest&) = delete;
 
-  ~BubbleDialogDelegateViewArrowTest() override = default;
+  ~BubbleDialogDelegateViewArrowTest() override {
+    display::Screen::SetScreenInstance(nullptr);
+  }
 
  private:
-  display::Screen* SetUpTestScreen() {
-    const display::Display test_display = test_screen_.GetPrimaryDisplay();
+  void SetUpTestScreen() {
+    DCHECK(!display::test::TestScreen::Get());
+    test_screen_ = std::make_unique<display::test::TestScreen>();
+    display::Screen::SetScreenInstance(test_screen_.get());
+    const display::Display test_display = test_screen_->GetPrimaryDisplay();
     display::Display display(test_display);
     display.set_id(0x2);
     display.set_bounds(gfx::Rect(0, 0, kScreenWidth, kScreenHeight));
     display.set_work_area(gfx::Rect(0, 0, kScreenWidth, kScreenHeight));
-    test_screen_.display_list().RemoveDisplay(test_display.id());
-    test_screen_.display_list().AddDisplay(display,
-                                           display::DisplayList::Type::PRIMARY);
-    return &test_screen_;
+    test_screen_->display_list().RemoveDisplay(test_display.id());
+    test_screen_->display_list().AddDisplay(
+        display, display::DisplayList::Type::PRIMARY);
   }
 
-  display::test::TestScreen test_screen_;
-  display::test::ScopedScreenOverride screen_override_;
+  std::unique_ptr<display::test::TestScreen> test_screen_;
 };
 
 TEST_P(BubbleDialogDelegateViewArrowTest, AvailableScreenSpaceTest) {
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index 4c219d938..34b4af7 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -24,6 +24,7 @@
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/base/ime/input_method.h"
 #import "ui/base/test/cocoa_helper.h"
+#include "ui/display/screen.h"
 #include "ui/events/test/cocoa_test_event_utils.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #import "ui/views/cocoa/native_widget_mac_ns_window_host.h"
@@ -385,6 +386,8 @@
 
  private:
   TestViewsDelegate test_views_delegate_;
+
+  display::ScopedNativeScreen screen_;
 };
 
 class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase,
diff --git a/ui/views/controls/base_control_test_widget.cc b/ui/views/controls/base_control_test_widget.cc
index 1bee91040..1131f0eb 100644
--- a/ui/views/controls/base_control_test_widget.cc
+++ b/ui/views/controls/base_control_test_widget.cc
@@ -23,8 +23,6 @@
 BaseControlTestWidget::~BaseControlTestWidget() = default;
 
 void BaseControlTestWidget::SetUp() {
-  ViewsTestBase::SetUp();
-
 #if BUILDFLAG(IS_MAC)
   test_screen_ = std::make_unique<display::test::TestScreenMac>(gfx::Size());
   // Purposely not use ScopedScreenOverride, in which GetScreen() will
@@ -32,6 +30,8 @@
   display::Screen::SetScreenInstance(test_screen_.get());
 #endif
 
+  ViewsTestBase::SetUp();
+
   widget_ = std::make_unique<Widget>();
   Widget::InitParams params =
       CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
@@ -47,10 +47,10 @@
 void BaseControlTestWidget::TearDown() {
   widget_.reset();
 
+  ViewsTestBase::TearDown();
 #if BUILDFLAG(IS_MAC)
   display::Screen::SetScreenInstance(nullptr);
 #endif
-  ViewsTestBase::TearDown();
 }
 
 void BaseControlTestWidget::CreateWidgetContent(View* container) {}
diff --git a/ui/views/test/test_desktop_screen_ozone.cc b/ui/views/test/test_desktop_screen_ozone.cc
index 7f018a7..61684a2 100644
--- a/ui/views/test/test_desktop_screen_ozone.cc
+++ b/ui/views/test/test_desktop_screen_ozone.cc
@@ -4,22 +4,20 @@
 
 #include "ui/views/test/test_desktop_screen_ozone.h"
 
-#include <memory>
-
-#include "base/memory/singleton.h"
-
-namespace views {
-namespace test {
-
+namespace views::test {
 namespace {
 TestDesktopScreenOzone* g_instance = nullptr;
 }
 
+// static
+std::unique_ptr<display::Screen> TestDesktopScreenOzone::Create() {
+  auto screen = std::make_unique<TestDesktopScreenOzone>();
+  screen->Initialize();
+  return screen;
+}
+
 TestDesktopScreenOzone* TestDesktopScreenOzone::GetInstance() {
-  if (!g_instance) {
-    g_instance = base::Singleton<TestDesktopScreenOzone>::get();
-    g_instance->Initialize();
-  }
+  DCHECK_EQ(display::Screen::GetScreen(), g_instance);
   return g_instance;
 }
 
@@ -27,8 +25,13 @@
   return cursor_screen_point_;
 }
 
-TestDesktopScreenOzone::TestDesktopScreenOzone() = default;
-TestDesktopScreenOzone::~TestDesktopScreenOzone() = default;
+TestDesktopScreenOzone::TestDesktopScreenOzone() {
+  DCHECK(!g_instance);
+  g_instance = this;
+}
 
-}  // namespace test
-}  // namespace views
+TestDesktopScreenOzone::~TestDesktopScreenOzone() {
+  g_instance = nullptr;
+}
+
+}  // namespace views::test
diff --git a/ui/views/test/test_desktop_screen_ozone.h b/ui/views/test/test_desktop_screen_ozone.h
index abaea86..1054c1c 100644
--- a/ui/views/test/test_desktop_screen_ozone.h
+++ b/ui/views/test/test_desktop_screen_ozone.h
@@ -5,12 +5,13 @@
 #ifndef UI_VIEWS_TEST_TEST_DESKTOP_SCREEN_OZONE_H_
 #define UI_VIEWS_TEST_TEST_DESKTOP_SCREEN_OZONE_H_
 
+#include <memory>
+
 #include "ui/gfx/geometry/point.h"
 #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
+namespace display {
+class Screen;
 }
 
 namespace views {
@@ -26,6 +27,7 @@
   TestDesktopScreenOzone(const TestDesktopScreenOzone&) = delete;
   TestDesktopScreenOzone& operator=(const TestDesktopScreenOzone&) = delete;
 
+  static std::unique_ptr<display::Screen> Create();
   static TestDesktopScreenOzone* GetInstance();
 
   // DesktopScreenOzone:
@@ -35,12 +37,10 @@
     cursor_screen_point_ = point;
   }
 
- private:
-  friend struct base::DefaultSingletonTraits<TestDesktopScreenOzone>;
-
   TestDesktopScreenOzone();
   ~TestDesktopScreenOzone() override;
 
+ private:
   gfx::Point cursor_screen_point_;
 };
 
diff --git a/ui/views/test/views_test_helper_mac.h b/ui/views/test/views_test_helper_mac.h
index a9abea1..3d5eb38 100644
--- a/ui/views/test/views_test_helper_mac.h
+++ b/ui/views/test/views_test_helper_mac.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/test/scoped_fake_full_keyboard_access.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/test_context_factories.h"
+#include "ui/display/screen.h"
 #include "ui/views/test/views_test_helper.h"
 
 namespace ui {
@@ -58,6 +60,8 @@
   // more consistent with other platforms, where most views are focusable by
   // default.
   ui::test::ScopedFakeFullKeyboardAccess faked_full_keyboard_access_;
+
+  display::ScopedNativeScreen screen_;
 };
 
 }  // namespace views
diff --git a/ui/views/test/widget_test.cc b/ui/views/test/widget_test.cc
index ac1e3a965..459e046 100644
--- a/ui/views/test/widget_test.cc
+++ b/ui/views/test/widget_test.cc
@@ -12,8 +12,12 @@
 #include "ui/views/test/native_widget_factory.h"
 #include "ui/views/widget/root_view.h"
 
-namespace views {
-namespace test {
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !BUILDFLAG(IS_CHROMECAST)
+#include "ui/views/test/test_desktop_screen_ozone.h"
+#endif
+
+namespace views::test {
 
 namespace {
 
@@ -130,9 +134,21 @@
 
 void DesktopWidgetTestInteractive::SetUp() {
   SetUpForInteractiveTests();
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !BUILDFLAG(IS_CHROMECAST)
+  screen_ = views::test::TestDesktopScreenOzone::Create();
+#endif
   DesktopWidgetTest::SetUp();
 }
 
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !BUILDFLAG(IS_CHROMECAST)
+void DesktopWidgetTestInteractive::TearDown() {
+  DesktopWidgetTest::TearDown();
+  screen_.reset();
+}
+#endif
+
 TestDesktopWidgetDelegate::TestDesktopWidgetDelegate()
     : TestDesktopWidgetDelegate(nullptr) {}
 
@@ -270,5 +286,4 @@
   widget_observation_.Reset();
 }
 
-}  // namespace test
-}  // namespace views
+}  // namespace views::test
diff --git a/ui/views/test/widget_test.h b/ui/views/test/widget_test.h
index 24b792f..1ba22f67 100644
--- a/ui/views/test/widget_test.h
+++ b/ui/views/test/widget_test.h
@@ -14,11 +14,17 @@
 #include "base/scoped_observation.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
+#include "build/chromecast_buildflags.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/widget/widget_observer.h"
 
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !BUILDFLAG(IS_CHROMECAST)
+#include "ui/display/screen.h"
+#endif
+
 namespace ui {
 namespace internal {
 class InputMethodDelegate;
@@ -156,6 +162,12 @@
 
   // DesktopWidgetTest
   void SetUp() override;
+
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !BUILDFLAG(IS_CHROMECAST)
+  void TearDown() override;
+  std::unique_ptr<display::Screen> screen_;
+#endif
 };
 
 // A helper WidgetDelegate for tests that require hooks into WidgetDelegate
diff --git a/ui/views/widget/desktop_aura/desktop_screen_win.cc b/ui/views/widget/desktop_aura/desktop_screen_win.cc
index 7c352dd..b5ad964 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_win.cc
@@ -13,10 +13,13 @@
 
 namespace views {
 
-DesktopScreenWin::DesktopScreenWin() = default;
+DesktopScreenWin::DesktopScreenWin() {
+  DCHECK(!display::Screen::HasScreen());
+  display::Screen::SetScreenInstance(this);
+}
 
 DesktopScreenWin::~DesktopScreenWin() {
-  display::Screen::SetScreenInstance(old_screen_);
+  display::Screen::SetScreenInstance(nullptr);
 }
 
 HWND DesktopScreenWin::GetHWNDFromNativeWindow(gfx::NativeWindow window) const {
diff --git a/ui/views/widget/desktop_aura/desktop_screen_win.h b/ui/views/widget/desktop_aura/desktop_screen_win.h
index 6ab217c..c2cbf5a 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_win.h
+++ b/ui/views/widget/desktop_aura/desktop_screen_win.h
@@ -23,9 +23,6 @@
   HWND GetHWNDFromNativeWindow(gfx::NativeWindow window) const override;
   gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override;
   bool IsNativeWindowOccluded(gfx::NativeWindow window) const override;
-
-  const raw_ptr<display::Screen> old_screen_ =
-      display::Screen::SetScreenInstance(this);
 };
 
 }  // namespace views
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn
index 660b5a3..d006f7979 100644
--- a/ui/wm/BUILD.gn
+++ b/ui/wm/BUILD.gn
@@ -100,10 +100,15 @@
   sources = [
     "test/testing_cursor_client_observer.cc",
     "test/testing_cursor_client_observer.h",
-    "test/wm_test_helper.cc",
-    "test/wm_test_helper.h",
   ]
 
+  if (is_chromeos_ash) {
+    sources += [
+      "test/wm_test_helper.cc",
+      "test/wm_test_helper.h",
+    ]
+  }
+
   public_deps = [ "//ui/base/cursor" ]
 
   deps = [
diff --git a/ui/wm/test/wm_test_helper.cc b/ui/wm/test/wm_test_helper.cc
index 5088317..6e09afb6 100644
--- a/ui/wm/test/wm_test_helper.cc
+++ b/ui/wm/test/wm_test_helper.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "build/build_config.h"
 #include "ui/aura/client/default_capture_client.h"
 #include "ui/aura/test/test_focus_client.h"
 #include "ui/aura/test/test_screen.h"
@@ -21,10 +22,10 @@
 
 WMTestHelper::WMTestHelper(const gfx::Size& default_window_size) {
   wm_state_ = std::make_unique<WMState>();
-
-  // Install a screen, like TestWindowService's AuraTestHelper for InitMusHost.
-  test_screen_ = base::WrapUnique(aura::TestScreen::Create(gfx::Size()));
-  display::Screen::SetScreenInstance(test_screen_.get());
+  if (!display::Screen::HasScreen()) {
+    test_screen_ = base::WrapUnique(aura::TestScreen::Create(gfx::Size()));
+    display::Screen::SetScreenInstance(test_screen_.get());
+  }
 
   host_ = aura::WindowTreeHost::Create(
       ui::PlatformWindowInitProperties{gfx::Rect(default_window_size)});
@@ -46,8 +47,7 @@
 
 WMTestHelper::~WMTestHelper() {
   host_->window()->RemovePreTargetHandler(root_window_event_filter_.get());
-
-  if (display::Screen::GetScreen() == test_screen_.get())
+  if (test_screen_)
     display::Screen::SetScreenInstance(nullptr);
 }